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