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 18 int is_valid_path(const char *path) 19 { 20 if (access(path, F_OK) == -1) 21 return 0; 22 return 1; 23 } 24 25 unsigned 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 46 unsigned 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 -1; 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 */ 75 int 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 */ 118 static 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 134 static 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 154 static 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 */ 168 int 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 248 void cpu_topology_release(struct cpupower_topology cpu_top) 249 { 250 free(cpu_top.core_info); 251 } 252