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