]>
Commit | Line | Data |
---|---|---|
dfd2257a | 1 | /* Determine various system internal values, Linux version. |
688903eb | 2 | Copyright (C) 1996-2018 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 | ||
a748eb31 | 88 | ssize_t n = __read_nocancel (fd, *re, buffer_end - *re); |
f43b4be6 UD |
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; | |
a748eb31 | 99 | n = __read_nocancel (fd, *re, buffer_end - *re); |
6a3d03ff UD |
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; |
c2284574 | 146 | int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags); |
84e2a551 UD |
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 | ||
c181840c | 183 | __close_nocancel_nostatus (fd); |
84e2a551 UD |
184 | |
185 | if (result > 0) | |
186 | goto out; | |
187 | } | |
188 | ||
189 | cp = buffer_end; | |
190 | re = buffer_end; | |
1ff6c67a FW |
191 | |
192 | /* Default to an SMP system in case we cannot obtain an accurate | |
193 | number. */ | |
194 | result = 2; | |
84e2a551 | 195 | |
9506149a | 196 | /* The /proc/stat format is more uniform, use it by default. */ |
c2284574 | 197 | fd = __open_nocancel ("/proc/stat", flags); |
f43b4be6 | 198 | if (fd != -1) |
9506149a | 199 | { |
9506149a | 200 | result = 0; |
f43b4be6 | 201 | |
f43b4be6 | 202 | while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) |
6a3d03ff UD |
203 | /* The current format of /proc/stat has all the cpu* entries |
204 | at the front. We assume here that stays this way. */ | |
205 | if (strncmp (l, "cpu", 3) != 0) | |
206 | break; | |
207 | else if (isdigit (l[3])) | |
9506149a UD |
208 | ++result; |
209 | ||
c181840c | 210 | __close_nocancel_nostatus (fd); |
9506149a UD |
211 | } |
212 | else | |
213 | { | |
c2284574 | 214 | fd = __open_nocancel ("/proc/cpuinfo", flags); |
f43b4be6 | 215 | if (fd != -1) |
9506149a | 216 | { |
f43b4be6 | 217 | GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); |
c181840c | 218 | __close_nocancel_nostatus (fd); |
9506149a UD |
219 | } |
220 | } | |
221 | ||
84e2a551 | 222 | out: |
852eb34d UD |
223 | cached_result = result; |
224 | atomic_write_barrier (); | |
d53a73ac | 225 | timestamp = now; |
852eb34d | 226 | |
9506149a UD |
227 | return result; |
228 | } | |
b5648377 | 229 | libc_hidden_def (__get_nprocs) |
845dcb57 UD |
230 | weak_alias (__get_nprocs, get_nprocs) |
231 | ||
bdb04ee8 | 232 | |
bdb04ee8 UD |
233 | /* On some architectures it is possible to distinguish between configured |
234 | and active cpus. */ | |
235 | int | |
60d2f8f3 | 236 | __get_nprocs_conf (void) |
bdb04ee8 | 237 | { |
bdb04ee8 UD |
238 | /* XXX Here will come a test for the new system call. */ |
239 | ||
9506149a UD |
240 | /* Try to use the sysfs filesystem. It has actual information about |
241 | online processors. */ | |
242 | DIR *dir = __opendir ("/sys/devices/system/cpu"); | |
243 | if (dir != NULL) | |
244 | { | |
245 | int count = 0; | |
246 | struct dirent64 *d; | |
247 | ||
248 | while ((d = __readdir64 (dir)) != NULL) | |
249 | /* NB: the sysfs has d_type support. */ | |
250 | if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu", 3) == 0) | |
251 | { | |
252 | char *endp; | |
253 | unsigned long int nr = strtoul (d->d_name + 3, &endp, 10); | |
254 | if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0') | |
255 | ++count; | |
256 | } | |
257 | ||
258 | __closedir (dir); | |
259 | ||
260 | return count; | |
261 | } | |
262 | ||
263 | int result = 1; | |
264 | ||
265 | #ifdef GET_NPROCS_CONF_PARSER | |
bdb04ee8 | 266 | /* If we haven't found an appropriate entry return 1. */ |
f43b4be6 | 267 | FILE *fp = fopen ("/proc/cpuinfo", "rce"); |
5c980df9 | 268 | if (fp != NULL) |
bdb04ee8 | 269 | { |
9506149a UD |
270 | char buffer[8192]; |
271 | ||
5c980df9 UD |
272 | /* No threads use this stream. */ |
273 | __fsetlocking (fp, FSETLOCKING_BYCALLER); | |
274 | GET_NPROCS_CONF_PARSER (fp, buffer, result); | |
275 | fclose (fp); | |
bdb04ee8 | 276 | } |
9506149a | 277 | #else |
0abf17de | 278 | result = __get_nprocs (); |
9506149a | 279 | #endif |
bdb04ee8 UD |
280 | |
281 | return result; | |
282 | } | |
b5648377 | 283 | libc_hidden_def (__get_nprocs_conf) |
bdb04ee8 | 284 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
845dcb57 | 285 | |
0ce657c5 RV |
286 | |
287 | /* Compute (num*mem_unit)/pagesize, but avoid overflowing long int. | |
288 | In practice, mem_unit is never bigger than the page size, so after | |
289 | the first loop it is 1. [In the kernel, it is initialized to | |
290 | PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in | |
291 | kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can | |
292 | represent all the sizes measured in bytes]. */ | |
e4cf5229 | 293 | static long int |
0ce657c5 | 294 | sysinfo_mempages (unsigned long int num, unsigned int mem_unit) |
845dcb57 | 295 | { |
0ce657c5 | 296 | unsigned long int ps = __getpagesize (); |
845dcb57 | 297 | |
0ce657c5 | 298 | while (mem_unit > 1 && ps > 1) |
845dcb57 | 299 | { |
0ce657c5 RV |
300 | mem_unit >>= 1; |
301 | ps >>= 1; | |
845dcb57 | 302 | } |
0ce657c5 RV |
303 | num *= mem_unit; |
304 | while (ps > 1) | |
305 | { | |
306 | ps >>= 1; | |
307 | num >>= 1; | |
308 | } | |
309 | return num; | |
845dcb57 UD |
310 | } |
311 | ||
0ce657c5 RV |
312 | /* Return the number of pages of total/available physical memory in |
313 | the system. This used to be done by parsing /proc/meminfo, but | |
314 | that's unnecessarily expensive (and /proc is not always available). | |
315 | The sysinfo syscall provides the same information, and has been | |
316 | available at least since kernel 2.3.48. */ | |
e4cf5229 | 317 | long int |
60d2f8f3 | 318 | __get_phys_pages (void) |
845dcb57 | 319 | { |
0ce657c5 | 320 | struct sysinfo info; |
845dcb57 | 321 | |
0ce657c5 RV |
322 | __sysinfo (&info); |
323 | return sysinfo_mempages (info.totalram, info.mem_unit); | |
845dcb57 | 324 | } |
b5648377 | 325 | libc_hidden_def (__get_phys_pages) |
845dcb57 UD |
326 | weak_alias (__get_phys_pages, get_phys_pages) |
327 | ||
e4cf5229 | 328 | long int |
60d2f8f3 | 329 | __get_avphys_pages (void) |
845dcb57 | 330 | { |
0ce657c5 | 331 | struct sysinfo info; |
845dcb57 | 332 | |
0ce657c5 RV |
333 | __sysinfo (&info); |
334 | return sysinfo_mempages (info.freeram, info.mem_unit); | |
845dcb57 | 335 | } |
b5648377 | 336 | libc_hidden_def (__get_avphys_pages) |
845dcb57 | 337 | weak_alias (__get_avphys_pages, get_avphys_pages) |