1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
4 */
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <stdlib.h>
13
14 #include "cpupower.h"
15 #include "cpupower_intern.h"
16
cpupower_read_sysfs(const char * path,char * buf,size_t buflen)17 unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
18 {
19 ssize_t numread;
20 int fd;
21
22 fd = open(path, O_RDONLY);
23 if (fd == -1)
24 return 0;
25
26 numread = read(fd, buf, buflen - 1);
27 if (numread < 1) {
28 close(fd);
29 return 0;
30 }
31
32 buf[numread] = '\0';
33 close(fd);
34
35 return (unsigned int) numread;
36 }
37
cpupower_write_sysfs(const char * path,char * buf,size_t buflen)38 unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen)
39 {
40 ssize_t numwritten;
41 int fd;
42
43 fd = open(path, O_WRONLY);
44 if (fd == -1)
45 return 0;
46
47 numwritten = write(fd, buf, buflen - 1);
48 if (numwritten < 1) {
49 perror(path);
50 close(fd);
51 return -1;
52 }
53
54 close(fd);
55
56 return (unsigned int) numwritten;
57 }
58
59 /*
60 * Detect whether a CPU is online
61 *
62 * Returns:
63 * 1 -> if CPU is online
64 * 0 -> if CPU is offline
65 * negative errno values in error case
66 */
cpupower_is_cpu_online(unsigned int cpu)67 int cpupower_is_cpu_online(unsigned int cpu)
68 {
69 char path[SYSFS_PATH_MAX];
70 int fd;
71 ssize_t numread;
72 unsigned long long value;
73 char linebuf[MAX_LINE_LEN];
74 char *endp;
75 struct stat statbuf;
76
77 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
78
79 if (stat(path, &statbuf) != 0)
80 return 0;
81
82 /*
83 * kernel without CONFIG_HOTPLUG_CPU
84 * -> cpuX directory exists, but not cpuX/online file
85 */
86 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
87 if (stat(path, &statbuf) != 0)
88 return 1;
89
90 fd = open(path, O_RDONLY);
91 if (fd == -1)
92 return -errno;
93
94 numread = read(fd, linebuf, MAX_LINE_LEN - 1);
95 if (numread < 1) {
96 close(fd);
97 return -EIO;
98 }
99 linebuf[numread] = '\0';
100 close(fd);
101
102 value = strtoull(linebuf, &endp, 0);
103 if (value > 1)
104 return -EINVAL;
105
106 return value;
107 }
108
109 /* returns -1 on failure, 0 on success */
sysfs_topology_read_file(unsigned int cpu,const char * fname,int * result)110 static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
111 {
112 char linebuf[MAX_LINE_LEN];
113 char *endp;
114 char path[SYSFS_PATH_MAX];
115
116 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
117 cpu, fname);
118 if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
119 return -1;
120 *result = strtol(linebuf, &endp, 0);
121 if (endp == linebuf || errno == ERANGE)
122 return -1;
123 return 0;
124 }
125
__compare(const void * t1,const void * t2)126 static int __compare(const void *t1, const void *t2)
127 {
128 struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
129 struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
130 if (top1->pkg < top2->pkg)
131 return -1;
132 else if (top1->pkg > top2->pkg)
133 return 1;
134 else if (top1->core < top2->core)
135 return -1;
136 else if (top1->core > top2->core)
137 return 1;
138 else if (top1->cpu < top2->cpu)
139 return -1;
140 else if (top1->cpu > top2->cpu)
141 return 1;
142 else
143 return 0;
144 }
145
146 /*
147 * Returns amount of cpus, negative on error, cpu_top must be
148 * passed to cpu_topology_release to free resources
149 *
150 * Array is sorted after ->pkg, ->core, then ->cpu
151 */
get_cpu_topology(struct cpupower_topology * cpu_top)152 int get_cpu_topology(struct cpupower_topology *cpu_top)
153 {
154 int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
155
156 cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
157 if (cpu_top->core_info == NULL)
158 return -ENOMEM;
159 cpu_top->pkgs = cpu_top->cores = 0;
160 for (cpu = 0; cpu < cpus; cpu++) {
161 cpu_top->core_info[cpu].cpu = cpu;
162 cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
163 if(sysfs_topology_read_file(
164 cpu,
165 "physical_package_id",
166 &(cpu_top->core_info[cpu].pkg)) < 0) {
167 cpu_top->core_info[cpu].pkg = -1;
168 cpu_top->core_info[cpu].core = -1;
169 continue;
170 }
171 if(sysfs_topology_read_file(
172 cpu,
173 "core_id",
174 &(cpu_top->core_info[cpu].core)) < 0) {
175 cpu_top->core_info[cpu].pkg = -1;
176 cpu_top->core_info[cpu].core = -1;
177 continue;
178 }
179 }
180
181 qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
182 __compare);
183
184 /* Count the number of distinct pkgs values. This works
185 because the primary sort of the core_info struct was just
186 done by pkg value. */
187 last_pkg = cpu_top->core_info[0].pkg;
188 for(cpu = 1; cpu < cpus; cpu++) {
189 if (cpu_top->core_info[cpu].pkg != last_pkg &&
190 cpu_top->core_info[cpu].pkg != -1) {
191
192 last_pkg = cpu_top->core_info[cpu].pkg;
193 cpu_top->pkgs++;
194 }
195 }
196 if (!(cpu_top->core_info[0].pkg == -1))
197 cpu_top->pkgs++;
198
199 /* Intel's cores count is not consecutively numbered, there may
200 * be a core_id of 3, but none of 2. Assume there always is 0
201 * Get amount of cores by counting duplicates in a package
202 for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) {
203 if (cpu_top->core_info[cpu].core == 0)
204 cpu_top->cores++;
205 */
206 return cpus;
207 }
208
cpu_topology_release(struct cpupower_topology cpu_top)209 void cpu_topology_release(struct cpupower_topology cpu_top)
210 {
211 free(cpu_top.core_info);
212 }
213