1 #include <errno.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include <sys/sysctl.h>
6 #include <sys/types.h>
7
8 #include <cpuinfo/log.h>
9 #include <freebsd/api.h>
10
sysctl_int(const char * name)11 static int sysctl_int(const char* name) {
12 int value = 0;
13 size_t value_size = sizeof(value);
14 if (sysctlbyname(name, &value, &value_size, NULL, 0) != 0) {
15 cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
16 } else if (value <= 0) {
17 cpuinfo_log_error("sysctlbyname(\"%s\") returned invalid value %d %zu", name, value, value_size);
18 value = 0;
19 }
20 return value;
21 }
22
sysctl_str(const char * name)23 static char* sysctl_str(const char* name) {
24 size_t value_size = 0;
25 if (sysctlbyname(name, NULL, &value_size, NULL, 0) != 0) {
26 cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
27 return NULL;
28 } else if (value_size <= 0) {
29 cpuinfo_log_error("sysctlbyname(\"%s\") returned invalid value size %zu", name, value_size);
30 return NULL;
31 }
32 value_size += 1;
33 char* value = calloc(value_size, 1);
34 if (!value) {
35 cpuinfo_log_error("calloc %zu bytes failed", value_size);
36 return NULL;
37 }
38 if (sysctlbyname(name, value, &value_size, NULL, 0) != 0) {
39 cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
40 free(value);
41 return NULL;
42 }
43 return value;
44 }
45
cpuinfo_freebsd_detect_topology(void)46 struct cpuinfo_freebsd_topology cpuinfo_freebsd_detect_topology(void) {
47 struct cpuinfo_freebsd_topology topology = {
48 .packages = 0,
49 .cores = 0,
50 .threads_per_core = 0,
51 .threads = 0,
52 };
53 char* topology_spec = sysctl_str("kern.sched.topology_spec");
54 if (!topology_spec) {
55 return topology;
56 }
57 const char* group_tags[] = {"<group level=\"2\" cache-level=\"0\">", "<group level=\"1\" "};
58 for (size_t i = 0; i < sizeof(group_tags) / sizeof(group_tags[0]); i++) {
59 const char* group_tag = group_tags[i];
60 char* p = strstr(topology_spec, group_tag);
61 while (p) {
62 topology.packages += 1;
63 p++;
64 p = strstr(p, group_tag);
65 }
66 if (topology.packages > 0) {
67 break;
68 }
69 }
70
71 if (topology.packages == 0) {
72 cpuinfo_log_error("failed to parse topology_spec: %s", topology_spec);
73 free(topology_spec);
74 goto fail;
75 }
76 free(topology_spec);
77 topology.cores = sysctl_int("kern.smp.cores");
78 if (topology.cores == 0) {
79 goto fail;
80 }
81 if (topology.cores < topology.packages) {
82 cpuinfo_log_error("invalid numbers of package and core: %d %d", topology.packages, topology.cores);
83 goto fail;
84 }
85 topology.threads_per_core = sysctl_int("kern.smp.threads_per_core");
86 if (topology.threads_per_core == 0) {
87 goto fail;
88 }
89 cpuinfo_log_debug(
90 "freebsd topology: packages = %d, cores = %d, "
91 "threads_per_core = %d",
92 topology.packages,
93 topology.cores,
94 topology.threads_per_core);
95 topology.threads = topology.threads_per_core * topology.cores;
96 return topology;
97 fail:
98 topology.packages = 0;
99 return topology;
100 }
101