File Index
Files and directories
Sign in
FILE

cpupower.c

File preview with syntax highlighting.

linux-6.19.14/tools/power/cpupower/lib/cpupower.c 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#include <string.h>
 14
 15#include "cpupower.h"
 16#include "cpupower_intern.h"
 17
 18int is_valid_path(const char *path)
 19{
 20	if (access(path, F_OK) == -1)
 21		return 0;
 22	return 1;
 23}
 24
 25unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
 26{
 27	ssize_t numread;
 28	int fd;
 29
 30	fd = open(path, O_RDONLY);
 31	if (fd == -1)
 32		return 0;
 33
 34	numread = read(fd, buf, buflen - 1);
 35	if (numread < 1) {
 36		close(fd);
 37		return 0;
 38	}
 39
 40	buf[numread] = '\0';
 41	close(fd);
 42
 43	return (unsigned int) numread;
 44}
 45
 46unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen)
 47{
 48	ssize_t numwritten;
 49	int fd;
 50
 51	fd = open(path, O_WRONLY);
 52	if (fd == -1)
 53		return 0;
 54
 55	numwritten = write(fd, buf, buflen - 1);
 56	if (numwritten < 1) {
 57		perror(path);
 58		close(fd);
 59		return 0;
 60	}
 61
 62	close(fd);
 63
 64	return (unsigned int) numwritten;
 65}
 66
 67/*
 68 * Detect whether a CPU is online
 69 *
 70 * Returns:
 71 *     1 -> if CPU is online
 72 *     0 -> if CPU is offline
 73 *     negative errno values in error case
 74 */
 75int cpupower_is_cpu_online(unsigned int cpu)
 76{
 77	char path[SYSFS_PATH_MAX];
 78	int fd;
 79	ssize_t numread;
 80	unsigned long long value;
 81	char linebuf[MAX_LINE_LEN];
 82	char *endp;
 83	struct stat statbuf;
 84
 85	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
 86
 87	if (stat(path, &statbuf) != 0)
 88		return 0;
 89
 90	/*
 91	 * kernel without CONFIG_HOTPLUG_CPU
 92	 * -> cpuX directory exists, but not cpuX/online file
 93	 */
 94	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
 95	if (stat(path, &statbuf) != 0)
 96		return 1;
 97
 98	fd = open(path, O_RDONLY);
 99	if (fd == -1)
100		return -errno;
101
102	numread = read(fd, linebuf, MAX_LINE_LEN - 1);
103	if (numread < 1) {
104		close(fd);
105		return -EIO;
106	}
107	linebuf[numread] = '\0';
108	close(fd);
109
110	value = strtoull(linebuf, &endp, 0);
111	if (value > 1)
112		return -EINVAL;
113
114	return value;
115}
116
117/* returns -1 on failure, 0 on success */
118static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
119{
120	char linebuf[MAX_LINE_LEN];
121	char *endp;
122	char path[SYSFS_PATH_MAX];
123
124	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
125			 cpu, fname);
126	if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
127		return -1;
128	*result = strtol(linebuf, &endp, 0);
129	if (endp == linebuf || errno == ERANGE)
130		return -1;
131	return 0;
132}
133
134static int __compare(const void *t1, const void *t2)
135{
136	struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
137	struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
138	if (top1->pkg < top2->pkg)
139		return -1;
140	else if (top1->pkg > top2->pkg)
141		return 1;
142	else if (top1->core < top2->core)
143		return -1;
144	else if (top1->core > top2->core)
145		return 1;
146	else if (top1->cpu < top2->cpu)
147		return -1;
148	else if (top1->cpu > top2->cpu)
149		return 1;
150	else
151		return 0;
152}
153
154static int __compare_core_cpu_list(const void *t1, const void *t2)
155{
156	struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
157	struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
158
159	return strcmp(top1->core_cpu_list, top2->core_cpu_list);
160}
161
162/*
163 * Returns amount of cpus, negative on error, cpu_top must be
164 * passed to cpu_topology_release to free resources
165 *
166 * Array is sorted after ->cpu_smt_list ->pkg, ->core
167 */
168int get_cpu_topology(struct cpupower_topology *cpu_top)
169{
170	int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
171	char path[SYSFS_PATH_MAX];
172	char *last_cpu_list;
173
174	cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
175	if (cpu_top->core_info == NULL)
176		return -ENOMEM;
177	cpu_top->pkgs = cpu_top->cores = 0;
178	for (cpu = 0; cpu < cpus; cpu++) {
179		cpu_top->core_info[cpu].cpu = cpu;
180		cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
181		if(sysfs_topology_read_file(
182			cpu,
183			"physical_package_id",
184			&(cpu_top->core_info[cpu].pkg)) < 0) {
185			cpu_top->core_info[cpu].pkg = -1;
186			cpu_top->core_info[cpu].core = -1;
187			continue;
188		}
189		if(sysfs_topology_read_file(
190			cpu,
191			"core_id",
192			&(cpu_top->core_info[cpu].core)) < 0) {
193			cpu_top->core_info[cpu].pkg = -1;
194			cpu_top->core_info[cpu].core = -1;
195			continue;
196		}
197		if (cpu_top->core_info[cpu].core == -1) {
198			strncpy(cpu_top->core_info[cpu].core_cpu_list, "-1", CPULIST_BUFFER);
199			continue;
200		}
201		snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
202			 cpu, "core_cpus_list");
203		if (cpupower_read_sysfs(
204			path,
205			cpu_top->core_info[cpu].core_cpu_list,
206			CPULIST_BUFFER) < 1) {
207			printf("Warning CPU%u has a 0 size core_cpus_list string", cpu);
208		}
209	}
210
211	/* Count the number of distinct cpu lists to get the physical core
212	 * count.
213	 */
214	qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
215	      __compare_core_cpu_list);
216
217	last_cpu_list = cpu_top->core_info[0].core_cpu_list;
218	cpu_top->cores = 1;
219	for (cpu = 1; cpu < cpus; cpu++) {
220		if (strcmp(cpu_top->core_info[cpu].core_cpu_list, last_cpu_list) != 0 &&
221		    cpu_top->core_info[cpu].pkg != -1) {
222			last_cpu_list = cpu_top->core_info[cpu].core_cpu_list;
223			cpu_top->cores++;
224		}
225	}
226
227	qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
228	      __compare);
229
230	/* Count the number of distinct pkgs values. This works
231	   because the primary sort of the core_info struct was just
232	   done by pkg value. */
233	last_pkg = cpu_top->core_info[0].pkg;
234	for(cpu = 1; cpu < cpus; cpu++) {
235		if (cpu_top->core_info[cpu].pkg != last_pkg &&
236				cpu_top->core_info[cpu].pkg != -1) {
237
238			last_pkg = cpu_top->core_info[cpu].pkg;
239			cpu_top->pkgs++;
240		}
241	}
242	if (!(cpu_top->core_info[0].pkg == -1))
243		cpu_top->pkgs++;
244
245	return cpus;
246}
247
248void cpu_topology_release(struct cpupower_topology cpu_top)
249{
250	free(cpu_top.core_info);
251}
~/ Read-only file preview.