GNU Linux-libre 5.10.217-gnu1
[releases.git] / tools / power / cpupower / lib / cpupower.c
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
17 unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
18 {
19         int fd;
20         ssize_t numread;
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
38 /*
39  * Detect whether a CPU is online
40  *
41  * Returns:
42  *     1 -> if CPU is online
43  *     0 -> if CPU is offline
44  *     negative errno values in error case
45  */
46 int cpupower_is_cpu_online(unsigned int cpu)
47 {
48         char path[SYSFS_PATH_MAX];
49         int fd;
50         ssize_t numread;
51         unsigned long long value;
52         char linebuf[MAX_LINE_LEN];
53         char *endp;
54         struct stat statbuf;
55
56         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
57
58         if (stat(path, &statbuf) != 0)
59                 return 0;
60
61         /*
62          * kernel without CONFIG_HOTPLUG_CPU
63          * -> cpuX directory exists, but not cpuX/online file
64          */
65         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
66         if (stat(path, &statbuf) != 0)
67                 return 1;
68
69         fd = open(path, O_RDONLY);
70         if (fd == -1)
71                 return -errno;
72
73         numread = read(fd, linebuf, MAX_LINE_LEN - 1);
74         if (numread < 1) {
75                 close(fd);
76                 return -EIO;
77         }
78         linebuf[numread] = '\0';
79         close(fd);
80
81         value = strtoull(linebuf, &endp, 0);
82         if (value > 1)
83                 return -EINVAL;
84
85         return value;
86 }
87
88 /* returns -1 on failure, 0 on success */
89 static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
90 {
91         char linebuf[MAX_LINE_LEN];
92         char *endp;
93         char path[SYSFS_PATH_MAX];
94
95         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
96                          cpu, fname);
97         if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
98                 return -1;
99         *result = strtol(linebuf, &endp, 0);
100         if (endp == linebuf || errno == ERANGE)
101                 return -1;
102         return 0;
103 }
104
105 static int __compare(const void *t1, const void *t2)
106 {
107         struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
108         struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
109         if (top1->pkg < top2->pkg)
110                 return -1;
111         else if (top1->pkg > top2->pkg)
112                 return 1;
113         else if (top1->core < top2->core)
114                 return -1;
115         else if (top1->core > top2->core)
116                 return 1;
117         else if (top1->cpu < top2->cpu)
118                 return -1;
119         else if (top1->cpu > top2->cpu)
120                 return 1;
121         else
122                 return 0;
123 }
124
125 /*
126  * Returns amount of cpus, negative on error, cpu_top must be
127  * passed to cpu_topology_release to free resources
128  *
129  * Array is sorted after ->pkg, ->core, then ->cpu
130  */
131 int get_cpu_topology(struct cpupower_topology *cpu_top)
132 {
133         int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
134
135         cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
136         if (cpu_top->core_info == NULL)
137                 return -ENOMEM;
138         cpu_top->pkgs = cpu_top->cores = 0;
139         for (cpu = 0; cpu < cpus; cpu++) {
140                 cpu_top->core_info[cpu].cpu = cpu;
141                 cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
142                 if(sysfs_topology_read_file(
143                         cpu,
144                         "physical_package_id",
145                         &(cpu_top->core_info[cpu].pkg)) < 0) {
146                         cpu_top->core_info[cpu].pkg = -1;
147                         cpu_top->core_info[cpu].core = -1;
148                         continue;
149                 }
150                 if(sysfs_topology_read_file(
151                         cpu,
152                         "core_id",
153                         &(cpu_top->core_info[cpu].core)) < 0) {
154                         cpu_top->core_info[cpu].pkg = -1;
155                         cpu_top->core_info[cpu].core = -1;
156                         continue;
157                 }
158         }
159
160         qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
161               __compare);
162
163         /* Count the number of distinct pkgs values. This works
164            because the primary sort of the core_info struct was just
165            done by pkg value. */
166         last_pkg = cpu_top->core_info[0].pkg;
167         for(cpu = 1; cpu < cpus; cpu++) {
168                 if (cpu_top->core_info[cpu].pkg != last_pkg &&
169                                 cpu_top->core_info[cpu].pkg != -1) {
170
171                         last_pkg = cpu_top->core_info[cpu].pkg;
172                         cpu_top->pkgs++;
173                 }
174         }
175         if (!(cpu_top->core_info[0].pkg == -1))
176                 cpu_top->pkgs++;
177
178         /* Intel's cores count is not consecutively numbered, there may
179          * be a core_id of 3, but none of 2. Assume there always is 0
180          * Get amount of cores by counting duplicates in a package
181         for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) {
182                 if (cpu_top->core_info[cpu].core == 0)
183         cpu_top->cores++;
184         */
185         return cpus;
186 }
187
188 void cpu_topology_release(struct cpupower_topology cpu_top)
189 {
190         free(cpu_top.core_info);
191 }