• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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