]>
Commit | Line | Data |
---|---|---|
ac5a181d TR |
1 | /* |
2 | * (C) 2004-2009 Dominik Brodowski <[email protected]> | |
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 | ||
9de9aa45 | 18 | unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen) |
ac5a181d TR |
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); | |
9de9aa45 | 98 | if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0) |
ac5a181d TR |
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 | } |