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 ssize_t numread; 20 int fd; 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 unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen) 39 { 40 ssize_t numwritten; 41 int fd; 42 43 fd = open(path, O_WRONLY); 44 if (fd == -1) 45 return 0; 46 47 numwritten = write(fd, buf, buflen - 1); 48 if (numwritten < 1) { 49 perror(path); 50 close(fd); 51 return -1; 52 } 53 54 close(fd); 55 56 return (unsigned int) numwritten; 57 } 58 59 /* 60 * Detect whether a CPU is online 61 * 62 * Returns: 63 * 1 -> if CPU is online 64 * 0 -> if CPU is offline 65 * negative errno values in error case 66 */ 67 int cpupower_is_cpu_online(unsigned int cpu) 68 { 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; 76 77 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); 78 79 if (stat(path, &statbuf) != 0) 80 return 0; 81 82 /* 83 * kernel without CONFIG_HOTPLUG_CPU 84 * -> cpuX directory exists, but not cpuX/online file 85 */ 86 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); 87 if (stat(path, &statbuf) != 0) 88 return 1; 89 90 fd = open(path, O_RDONLY); 91 if (fd == -1) 92 return -errno; 93 94 numread = read(fd, linebuf, MAX_LINE_LEN - 1); 95 if (numread < 1) { 96 close(fd); 97 return -EIO; 98 } 99 linebuf[numread] = '\0'; 100 close(fd); 101 102 value = strtoull(linebuf, &endp, 0); 103 if (value > 1) 104 return -EINVAL; 105 106 return value; 107 } 108 109 /* returns -1 on failure, 0 on success */ 110 static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result) 111 { 112 char linebuf[MAX_LINE_LEN]; 113 char *endp; 114 char path[SYSFS_PATH_MAX]; 115 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; 124 } 125 126 static int __compare(const void *t1, const void *t2) 127 { 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; 144 } 145 146 /* 147 * Returns amount of cpus, negative on error, cpu_top must be 148 * passed to cpu_topology_release to free resources 149 * 150 * Array is sorted after ->pkg, ->core, then ->cpu 151 */ 152 int get_cpu_topology(struct cpupower_topology *cpu_top) 153 { 154 int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); 155 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; 170 } 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; 178 } 179 } 180 181 qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), 182 __compare); 183 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) { 191 192 last_pkg = cpu_top->core_info[cpu].pkg; 193 cpu_top->pkgs++; 194 } 195 } 196 if (!(cpu_top->core_info[0].pkg == -1)) 197 cpu_top->pkgs++; 198 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++; 205 */ 206 return cpus; 207 } 208 209 void cpu_topology_release(struct cpupower_topology cpu_top) 210 { 211 free(cpu_top.core_info); 212 } 213