1 | /* Determine various system internal values, Linux version. |
2 | Copyright (C) 1996-2017 Free Software Foundation, Inc. |
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 |
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. |
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 |
14 | Lesser General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with the GNU C Library; if not, see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | #include <alloca.h> |
21 | #include <assert.h> |
22 | #include <ctype.h> |
23 | #include <dirent.h> |
24 | #include <errno.h> |
25 | #include <fcntl.h> |
26 | #include <mntent.h> |
27 | #include <paths.h> |
28 | #include <stdio.h> |
29 | #include <stdio_ext.h> |
30 | #include <stdlib.h> |
31 | #include <string.h> |
32 | #include <unistd.h> |
33 | #include <sys/sysinfo.h> |
34 | |
35 | #include <atomic.h> |
36 | #include <not-cancel.h> |
37 | |
38 | |
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 |
45 | examine the pseudo file /proc/cpuinfo. Here we have one entry for |
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. */ |
50 | |
51 | |
52 | /* Other architectures use different formats for /proc/cpuinfo. This |
53 | provides a hook for alternative parsers. */ |
54 | #ifndef GET_NPROCS_PARSER |
55 | # define GET_NPROCS_PARSER(FD, BUFFER, CP, RE, BUFFER_END, RESULT) \ |
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. */ \ |
63 | char *l; \ |
64 | while ((l = next_line (FD, BUFFER, &CP, &RE, BUFFER_END)) != NULL) \ |
65 | if (strncmp (l, "processor", 9) == 0) \ |
66 | ++(RESULT); \ |
67 | } \ |
68 | while (0) |
69 | #endif |
70 | |
71 | |
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; |
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 | } |
107 | } |
108 | else |
109 | nl = memchr (*cp, '\n', *re - *cp); |
110 | |
111 | res = *cp; |
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 | |
125 | int |
126 | __get_nprocs (void) |
127 | { |
128 | static int cached_result = -1; |
129 | static time_t timestamp; |
130 | |
131 | time_t now = time (NULL); |
132 | time_t prev = timestamp; |
133 | atomic_read_barrier (); |
134 | if (now == prev && cached_result > -1) |
135 | return cached_result; |
136 | |
137 | /* XXX Here will come a test for the new system call. */ |
138 | |
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; |
142 | char *cp = buffer_end; |
143 | char *re = buffer_end; |
144 | |
145 | const int flags = O_RDONLY | O_CLOEXEC; |
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 | |
192 | /* Default to an SMP system in case we cannot obtain an accurate |
193 | number. */ |
194 | result = 2; |
195 | |
196 | /* The /proc/stat format is more uniform, use it by default. */ |
197 | fd = open_not_cancel_2 ("/proc/stat" , flags); |
198 | if (fd != -1) |
199 | { |
200 | result = 0; |
201 | |
202 | while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) |
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])) |
208 | ++result; |
209 | |
210 | close_not_cancel_no_status (fd); |
211 | } |
212 | else |
213 | { |
214 | fd = open_not_cancel_2 ("/proc/cpuinfo" , flags); |
215 | if (fd != -1) |
216 | { |
217 | GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); |
218 | close_not_cancel_no_status (fd); |
219 | } |
220 | } |
221 | |
222 | out: |
223 | cached_result = result; |
224 | atomic_write_barrier (); |
225 | timestamp = now; |
226 | |
227 | return result; |
228 | } |
229 | weak_alias (__get_nprocs, get_nprocs) |
230 | |
231 | |
232 | /* On some architectures it is possible to distinguish between configured |
233 | and active cpus. */ |
234 | int |
235 | __get_nprocs_conf (void) |
236 | { |
237 | /* XXX Here will come a test for the new system call. */ |
238 | |
239 | /* Try to use the sysfs filesystem. It has actual information about |
240 | online processors. */ |
241 | DIR *dir = __opendir ("/sys/devices/system/cpu" ); |
242 | if (dir != NULL) |
243 | { |
244 | int count = 0; |
245 | struct dirent64 *d; |
246 | |
247 | while ((d = __readdir64 (dir)) != NULL) |
248 | /* NB: the sysfs has d_type support. */ |
249 | if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu" , 3) == 0) |
250 | { |
251 | char *endp; |
252 | unsigned long int nr = strtoul (d->d_name + 3, &endp, 10); |
253 | if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0') |
254 | ++count; |
255 | } |
256 | |
257 | __closedir (dir); |
258 | |
259 | return count; |
260 | } |
261 | |
262 | int result = 1; |
263 | |
264 | #ifdef GET_NPROCS_CONF_PARSER |
265 | /* If we haven't found an appropriate entry return 1. */ |
266 | FILE *fp = fopen ("/proc/cpuinfo" , "rce" ); |
267 | if (fp != NULL) |
268 | { |
269 | char buffer[8192]; |
270 | |
271 | /* No threads use this stream. */ |
272 | __fsetlocking (fp, FSETLOCKING_BYCALLER); |
273 | GET_NPROCS_CONF_PARSER (fp, buffer, result); |
274 | fclose (fp); |
275 | } |
276 | #else |
277 | result = __get_nprocs (); |
278 | #endif |
279 | |
280 | return result; |
281 | } |
282 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
283 | |
284 | |
285 | /* Compute (num*mem_unit)/pagesize, but avoid overflowing long int. |
286 | In practice, mem_unit is never bigger than the page size, so after |
287 | the first loop it is 1. [In the kernel, it is initialized to |
288 | PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in |
289 | kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can |
290 | represent all the sizes measured in bytes]. */ |
291 | static long int |
292 | sysinfo_mempages (unsigned long int num, unsigned int mem_unit) |
293 | { |
294 | unsigned long int ps = __getpagesize (); |
295 | |
296 | while (mem_unit > 1 && ps > 1) |
297 | { |
298 | mem_unit >>= 1; |
299 | ps >>= 1; |
300 | } |
301 | num *= mem_unit; |
302 | while (ps > 1) |
303 | { |
304 | ps >>= 1; |
305 | num >>= 1; |
306 | } |
307 | return num; |
308 | } |
309 | |
310 | /* Return the number of pages of total/available physical memory in |
311 | the system. This used to be done by parsing /proc/meminfo, but |
312 | that's unnecessarily expensive (and /proc is not always available). |
313 | The sysinfo syscall provides the same information, and has been |
314 | available at least since kernel 2.3.48. */ |
315 | long int |
316 | __get_phys_pages (void) |
317 | { |
318 | struct sysinfo info; |
319 | |
320 | __sysinfo (&info); |
321 | return sysinfo_mempages (info.totalram, info.mem_unit); |
322 | } |
323 | weak_alias (__get_phys_pages, get_phys_pages) |
324 | |
325 | long int |
326 | __get_avphys_pages (void) |
327 | { |
328 | struct sysinfo info; |
329 | |
330 | __sysinfo (&info); |
331 | return sysinfo_mempages (info.freeram, info.mem_unit); |
332 | } |
333 | weak_alias (__get_avphys_pages, get_avphys_pages) |
334 | |