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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |