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
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
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
cpuinfo_get_processors(void)50 const 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
cpuinfo_get_cores(void)57 const 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
cpuinfo_get_clusters(void)64 const 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
cpuinfo_get_packages(void)71 const 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
cpuinfo_get_uarchs()78 const 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
cpuinfo_get_processor(uint32_t index)89 const 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
cpuinfo_get_core(uint32_t index)99 const 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
cpuinfo_get_cluster(uint32_t index)109 const 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
cpuinfo_get_package(uint32_t index)119 const 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
cpuinfo_get_uarch(uint32_t index)129 const 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
cpuinfo_get_processors_count(void)146 uint32_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
cpuinfo_get_cores_count(void)153 uint32_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
cpuinfo_get_clusters_count(void)160 uint32_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
cpuinfo_get_packages_count(void)167 uint32_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
cpuinfo_get_uarchs_count(void)174 uint32_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
cpuinfo_get_l1i_caches(void)185 const 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
cpuinfo_get_l1d_caches(void)192 const 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
cpuinfo_get_l2_caches(void)199 const 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
cpuinfo_get_l3_caches(void)206 const 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
cpuinfo_get_l4_caches(void)213 const 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
cpuinfo_get_l1i_cache(uint32_t index)220 const 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
cpuinfo_get_l1d_cache(uint32_t index)230 const 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
cpuinfo_get_l2_cache(uint32_t index)240 const 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
cpuinfo_get_l3_cache(uint32_t index)250 const 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
cpuinfo_get_l4_cache(uint32_t index)260 const 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
cpuinfo_get_l1i_caches_count(void)270 uint32_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
cpuinfo_get_l1d_caches_count(void)277 uint32_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
cpuinfo_get_l2_caches_count(void)284 uint32_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
cpuinfo_get_l3_caches_count(void)291 uint32_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
cpuinfo_get_l4_caches_count(void)298 uint32_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
cpuinfo_get_max_cache_size(void)305 uint32_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
cpuinfo_get_current_processor(void)312 const 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
cpuinfo_get_current_core(void)331 const 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
cpuinfo_get_current_uarch_index(void)350 uint32_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
cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index)381 uint32_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