Merge tag 'ntb-5.11' of git://github.com/jonmason/ntb
[linux/fpc-iii.git] / tools / power / cpupower / lib / cpupower.c
blob3f7d0c0c50676a22370c78b5b3bbd82d6c108b61
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
4 */
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>
14 #include "cpupower.h"
15 #include "cpupower_intern.h"
17 unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
19 ssize_t numread;
20 int fd;
22 fd = open(path, O_RDONLY);
23 if (fd == -1)
24 return 0;
26 numread = read(fd, buf, buflen - 1);
27 if (numread < 1) {
28 close(fd);
29 return 0;
32 buf[numread] = '\0';
33 close(fd);
35 return (unsigned int) numread;
38 unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen)
40 ssize_t numwritten;
41 int fd;
43 fd = open(path, O_WRONLY);
44 if (fd == -1)
45 return 0;
47 numwritten = write(fd, buf, buflen - 1);
48 if (numwritten < 1) {
49 perror(path);
50 close(fd);
51 return -1;
54 close(fd);
56 return (unsigned int) numwritten;
60 * Detect whether a CPU is online
62 * Returns:
63 * 1 -> if CPU is online
64 * 0 -> if CPU is offline
65 * negative errno values in error case
67 int cpupower_is_cpu_online(unsigned int cpu)
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;
77 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
79 if (stat(path, &statbuf) != 0)
80 return 0;
83 * kernel without CONFIG_HOTPLUG_CPU
84 * -> cpuX directory exists, but not cpuX/online file
86 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
87 if (stat(path, &statbuf) != 0)
88 return 1;
90 fd = open(path, O_RDONLY);
91 if (fd == -1)
92 return -errno;
94 numread = read(fd, linebuf, MAX_LINE_LEN - 1);
95 if (numread < 1) {
96 close(fd);
97 return -EIO;
99 linebuf[numread] = '\0';
100 close(fd);
102 value = strtoull(linebuf, &endp, 0);
103 if (value > 1)
104 return -EINVAL;
106 return value;
109 /* returns -1 on failure, 0 on success */
110 static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
112 char linebuf[MAX_LINE_LEN];
113 char *endp;
114 char path[SYSFS_PATH_MAX];
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;
126 static int __compare(const void *t1, const void *t2)
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;
147 * Returns amount of cpus, negative on error, cpu_top must be
148 * passed to cpu_topology_release to free resources
150 * Array is sorted after ->pkg, ->core, then ->cpu
152 int get_cpu_topology(struct cpupower_topology *cpu_top)
154 int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
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;
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;
181 qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
182 __compare);
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) {
192 last_pkg = cpu_top->core_info[cpu].pkg;
193 cpu_top->pkgs++;
196 if (!(cpu_top->core_info[0].pkg == -1))
197 cpu_top->pkgs++;
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++;
206 return cpus;
209 void cpu_topology_release(struct cpupower_topology cpu_top)
211 free(cpu_top.core_info);