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