]>
Commit | Line | Data |
---|---|---|
dfd2257a | 1 | /* Determine various system internal values, Linux version. |
b168057a | 2 | Copyright (C) 1996-2015 Free Software Foundation, Inc. |
84384f5b UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. | |
5 | ||
6 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
84384f5b UD |
10 | |
11 | The GNU C Library is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
41bdb6e2 | 14 | Lesser General Public License for more details. |
84384f5b | 15 | |
41bdb6e2 | 16 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
17 | License along with the GNU C Library; if not, see |
18 | <http://www.gnu.org/licenses/>. */ | |
84384f5b UD |
19 | |
20 | #include <alloca.h> | |
77432371 | 21 | #include <assert.h> |
27692f89 | 22 | #include <ctype.h> |
58d43ba8 | 23 | #include <dirent.h> |
845dcb57 | 24 | #include <errno.h> |
58d43ba8 | 25 | #include <fcntl.h> |
845dcb57 | 26 | #include <mntent.h> |
46ec036d | 27 | #include <paths.h> |
845dcb57 | 28 | #include <stdio.h> |
2706ee38 | 29 | #include <stdio_ext.h> |
77432371 | 30 | #include <stdlib.h> |
845dcb57 UD |
31 | #include <string.h> |
32 | #include <unistd.h> | |
33 | #include <sys/sysinfo.h> | |
34 | ||
4009bf40 | 35 | #include <atomic.h> |
58d43ba8 | 36 | #include <not-cancel.h> |
852eb34d | 37 | |
77432371 | 38 | |
845dcb57 UD |
39 | /* How we can determine the number of available processors depends on |
40 | the configuration. There is currently (as of version 2.0.21) no | |
41 | system call to determine the number. It is planned for the 2.1.x | |
42 | series to add this, though. | |
43 | ||
44 | One possibility to implement it for systems using Linux 2.0 is to | |
b0b67c47 | 45 | examine the pseudo file /proc/cpuinfo. Here we have one entry for |
845dcb57 UD |
46 | each processor. |
47 | ||
48 | But not all systems have support for the /proc filesystem. If it | |
49 | is not available we simply return 1 since there is no way. */ | |
bdb04ee8 | 50 | |
f43b4be6 | 51 | |
bdb04ee8 UD |
52 | /* Other architectures use different formats for /proc/cpuinfo. This |
53 | provides a hook for alternative parsers. */ | |
54 | #ifndef GET_NPROCS_PARSER | |
f43b4be6 | 55 | # define GET_NPROCS_PARSER(FD, BUFFER, CP, RE, BUFFER_END, RESULT) \ |
bdb04ee8 UD |
56 | do \ |
57 | { \ | |
58 | (RESULT) = 0; \ | |
59 | /* Read all lines and count the lines starting with the string \ | |
60 | "processor". We don't have to fear extremely long lines since \ | |
61 | the kernel will not generate them. 8192 bytes are really \ | |
62 | enough. */ \ | |
f43b4be6 UD |
63 | char *l; \ |
64 | while ((l = next_line (FD, BUFFER, &CP, &RE, BUFFER_END)) != NULL) \ | |
65 | if (strncmp (l, "processor", 9) == 0) \ | |
bdb04ee8 UD |
66 | ++(RESULT); \ |
67 | } \ | |
68 | while (0) | |
69 | #endif | |
70 | ||
9506149a | 71 | |
f43b4be6 UD |
72 | static char * |
73 | next_line (int fd, char *const buffer, char **cp, char **re, | |
74 | char *const buffer_end) | |
75 | { | |
76 | char *res = *cp; | |
77 | char *nl = memchr (*cp, '\n', *re - *cp); | |
78 | if (nl == NULL) | |
79 | { | |
80 | if (*cp != buffer) | |
81 | { | |
82 | if (*re == buffer_end) | |
83 | { | |
84 | memmove (buffer, *cp, *re - *cp); | |
85 | *re = buffer + (*re - *cp); | |
86 | *cp = buffer; | |
87 | ||
88 | ssize_t n = read_not_cancel (fd, *re, buffer_end - *re); | |
89 | if (n < 0) | |
90 | return NULL; | |
91 | ||
92 | *re += n; | |
6a3d03ff UD |
93 | |
94 | nl = memchr (*cp, '\n', *re - *cp); | |
95 | while (nl == NULL && *re == buffer_end) | |
96 | { | |
97 | /* Truncate too long lines. */ | |
98 | *re = buffer + 3 * (buffer_end - buffer) / 4; | |
99 | n = read_not_cancel (fd, *re, buffer_end - *re); | |
100 | if (n < 0) | |
101 | return NULL; | |
102 | ||
103 | nl = memchr (*re, '\n', n); | |
104 | **re = '\n'; | |
105 | *re += n; | |
106 | } | |
f43b4be6 | 107 | } |
6a3d03ff UD |
108 | else |
109 | nl = memchr (*cp, '\n', *re - *cp); | |
f43b4be6 UD |
110 | |
111 | res = *cp; | |
f43b4be6 UD |
112 | } |
113 | ||
114 | if (nl == NULL) | |
115 | nl = *re - 1; | |
116 | } | |
117 | ||
118 | *cp = nl + 1; | |
119 | assert (*cp <= *re); | |
120 | ||
121 | return res == *re ? NULL : res; | |
122 | } | |
123 | ||
124 | ||
0abf17de | 125 | int |
60d2f8f3 | 126 | __get_nprocs (void) |
845dcb57 | 127 | { |
995a46bb | 128 | static int cached_result = -1; |
852eb34d UD |
129 | static time_t timestamp; |
130 | ||
d53a73ac | 131 | time_t now = time (NULL); |
852eb34d UD |
132 | time_t prev = timestamp; |
133 | atomic_read_barrier (); | |
995a46bb | 134 | if (now == prev && cached_result > -1) |
852eb34d UD |
135 | return cached_result; |
136 | ||
0abf17de UD |
137 | /* XXX Here will come a test for the new system call. */ |
138 | ||
6a3d03ff UD |
139 | const size_t buffer_size = __libc_use_alloca (8192) ? 8192 : 512; |
140 | char *buffer = alloca (buffer_size); | |
141 | char *buffer_end = buffer + buffer_size; | |
f43b4be6 UD |
142 | char *cp = buffer_end; |
143 | char *re = buffer_end; | |
845dcb57 | 144 | |
f43b4be6 | 145 | const int flags = O_RDONLY | O_CLOEXEC; |
84e2a551 UD |
146 | int fd = open_not_cancel_2 ("/sys/devices/system/cpu/online", flags); |
147 | char *l; | |
148 | int result = 0; | |
149 | if (fd != -1) | |
150 | { | |
151 | l = next_line (fd, buffer, &cp, &re, buffer_end); | |
152 | if (l != NULL) | |
153 | do | |
154 | { | |
155 | char *endp; | |
156 | unsigned long int n = strtoul (l, &endp, 10); | |
157 | if (l == endp) | |
158 | { | |
159 | result = 0; | |
160 | break; | |
161 | } | |
162 | ||
163 | unsigned long int m = n; | |
164 | if (*endp == '-') | |
165 | { | |
166 | l = endp + 1; | |
167 | m = strtoul (l, &endp, 10); | |
168 | if (l == endp) | |
169 | { | |
170 | result = 0; | |
171 | break; | |
172 | } | |
173 | } | |
174 | ||
175 | result += m - n + 1; | |
176 | ||
177 | l = endp; | |
178 | while (l < re && isspace (*l)) | |
179 | ++l; | |
180 | } | |
181 | while (l < re); | |
182 | ||
183 | close_not_cancel_no_status (fd); | |
184 | ||
185 | if (result > 0) | |
186 | goto out; | |
187 | } | |
188 | ||
189 | cp = buffer_end; | |
190 | re = buffer_end; | |
191 | result = 1; | |
192 | ||
9506149a | 193 | /* The /proc/stat format is more uniform, use it by default. */ |
84e2a551 | 194 | fd = open_not_cancel_2 ("/proc/stat", flags); |
f43b4be6 | 195 | if (fd != -1) |
9506149a | 196 | { |
9506149a | 197 | result = 0; |
f43b4be6 | 198 | |
f43b4be6 | 199 | while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) |
6a3d03ff UD |
200 | /* The current format of /proc/stat has all the cpu* entries |
201 | at the front. We assume here that stays this way. */ | |
202 | if (strncmp (l, "cpu", 3) != 0) | |
203 | break; | |
204 | else if (isdigit (l[3])) | |
9506149a UD |
205 | ++result; |
206 | ||
f43b4be6 | 207 | close_not_cancel_no_status (fd); |
9506149a UD |
208 | } |
209 | else | |
210 | { | |
f43b4be6 UD |
211 | fd = open_not_cancel_2 ("/proc/cpuinfo", flags); |
212 | if (fd != -1) | |
9506149a | 213 | { |
f43b4be6 UD |
214 | GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); |
215 | close_not_cancel_no_status (fd); | |
9506149a UD |
216 | } |
217 | } | |
218 | ||
84e2a551 | 219 | out: |
852eb34d UD |
220 | cached_result = result; |
221 | atomic_write_barrier (); | |
d53a73ac | 222 | timestamp = now; |
852eb34d | 223 | |
9506149a UD |
224 | return result; |
225 | } | |
845dcb57 UD |
226 | weak_alias (__get_nprocs, get_nprocs) |
227 | ||
bdb04ee8 | 228 | |
bdb04ee8 UD |
229 | /* On some architectures it is possible to distinguish between configured |
230 | and active cpus. */ | |
231 | int | |
60d2f8f3 | 232 | __get_nprocs_conf (void) |
bdb04ee8 | 233 | { |
bdb04ee8 UD |
234 | /* XXX Here will come a test for the new system call. */ |
235 | ||
9506149a UD |
236 | /* Try to use the sysfs filesystem. It has actual information about |
237 | online processors. */ | |
238 | DIR *dir = __opendir ("/sys/devices/system/cpu"); | |
239 | if (dir != NULL) | |
240 | { | |
241 | int count = 0; | |
242 | struct dirent64 *d; | |
243 | ||
244 | while ((d = __readdir64 (dir)) != NULL) | |
245 | /* NB: the sysfs has d_type support. */ | |
246 | if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu", 3) == 0) | |
247 | { | |
248 | char *endp; | |
249 | unsigned long int nr = strtoul (d->d_name + 3, &endp, 10); | |
250 | if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0') | |
251 | ++count; | |
252 | } | |
253 | ||
254 | __closedir (dir); | |
255 | ||
256 | return count; | |
257 | } | |
258 | ||
259 | int result = 1; | |
260 | ||
261 | #ifdef GET_NPROCS_CONF_PARSER | |
bdb04ee8 | 262 | /* If we haven't found an appropriate entry return 1. */ |
f43b4be6 | 263 | FILE *fp = fopen ("/proc/cpuinfo", "rce"); |
5c980df9 | 264 | if (fp != NULL) |
bdb04ee8 | 265 | { |
9506149a UD |
266 | char buffer[8192]; |
267 | ||
5c980df9 UD |
268 | /* No threads use this stream. */ |
269 | __fsetlocking (fp, FSETLOCKING_BYCALLER); | |
270 | GET_NPROCS_CONF_PARSER (fp, buffer, result); | |
271 | fclose (fp); | |
bdb04ee8 | 272 | } |
9506149a | 273 | #else |
0abf17de | 274 | result = __get_nprocs (); |
9506149a | 275 | #endif |
bdb04ee8 UD |
276 | |
277 | return result; | |
278 | } | |
bdb04ee8 | 279 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
845dcb57 UD |
280 | |
281 | /* General function to get information about memory status from proc | |
282 | filesystem. */ | |
e4cf5229 | 283 | static long int |
dfd2257a | 284 | internal_function |
845dcb57 UD |
285 | phys_pages_info (const char *format) |
286 | { | |
845dcb57 | 287 | char buffer[8192]; |
e4cf5229 | 288 | long int result = -1; |
845dcb57 | 289 | |
845dcb57 | 290 | /* If we haven't found an appropriate entry return 1. */ |
312be3f9 | 291 | FILE *fp = fopen ("/proc/meminfo", "rce"); |
5c980df9 | 292 | if (fp != NULL) |
845dcb57 | 293 | { |
5c980df9 UD |
294 | /* No threads use this stream. */ |
295 | __fsetlocking (fp, FSETLOCKING_BYCALLER); | |
296 | ||
297 | result = 0; | |
298 | /* Read all lines and count the lines starting with the | |
299 | string "processor". We don't have to fear extremely long | |
300 | lines since the kernel will not generate them. 8192 | |
301 | bytes are really enough. */ | |
c4eae752 | 302 | while (__fgets_unlocked (buffer, sizeof buffer, fp) != NULL) |
5c980df9 UD |
303 | if (sscanf (buffer, format, &result) == 1) |
304 | { | |
305 | result /= (__getpagesize () / 1024); | |
306 | break; | |
307 | } | |
a334319f | 308 | |
5c980df9 | 309 | fclose (fp); |
845dcb57 UD |
310 | } |
311 | ||
312 | if (result == -1) | |
313 | /* We cannot get the needed value: signal an error. */ | |
c4029823 | 314 | __set_errno (ENOSYS); |
845dcb57 UD |
315 | |
316 | return result; | |
317 | } | |
318 | ||
319 | ||
320 | /* Return the number of pages of physical memory in the system. There | |
321 | is currently (as of version 2.0.21) no system call to determine the | |
322 | number. It is planned for the 2.1.x series to add this, though. | |
323 | ||
324 | One possibility to implement it for systems using Linux 2.0 is to | |
325 | examine the pseudo file /proc/cpuinfo. Here we have one entry for | |
326 | each processor. | |
327 | ||
328 | But not all systems have support for the /proc filesystem. If it | |
329 | is not available we return -1 as an error signal. */ | |
e4cf5229 | 330 | long int |
60d2f8f3 | 331 | __get_phys_pages (void) |
845dcb57 UD |
332 | { |
333 | /* XXX Here will come a test for the new system call. */ | |
334 | ||
e4cf5229 | 335 | return phys_pages_info ("MemTotal: %ld kB"); |
845dcb57 UD |
336 | } |
337 | weak_alias (__get_phys_pages, get_phys_pages) | |
338 | ||
339 | ||
340 | /* Return the number of available pages of physical memory in the | |
341 | system. There is currently (as of version 2.0.21) no system call | |
342 | to determine the number. It is planned for the 2.1.x series to add | |
343 | this, though. | |
344 | ||
345 | One possibility to implement it for systems using Linux 2.0 is to | |
346 | examine the pseudo file /proc/cpuinfo. Here we have one entry for | |
347 | each processor. | |
348 | ||
349 | But not all systems have support for the /proc filesystem. If it | |
350 | is not available we return -1 as an error signal. */ | |
e4cf5229 | 351 | long int |
60d2f8f3 | 352 | __get_avphys_pages (void) |
845dcb57 UD |
353 | { |
354 | /* XXX Here will come a test for the new system call. */ | |
355 | ||
e4cf5229 | 356 | return phys_pages_info ("MemFree: %ld kB"); |
845dcb57 UD |
357 | } |
358 | weak_alias (__get_avphys_pages, get_avphys_pages) |