1 | /* Determine various system internal values, Linux version. |
2 | Copyright (C) 1996-2019 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_nocancel (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_nocancel (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_nocancel ("/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_nocancel_nostatus (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_nocancel ("/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_nocancel_nostatus (fd); |
211 | } |
212 | else |
213 | { |
214 | fd = __open_nocancel ("/proc/cpuinfo" , flags); |
215 | if (fd != -1) |
216 | { |
217 | GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); |
218 | __close_nocancel_nostatus (fd); |
219 | } |
220 | } |
221 | |
222 | out: |
223 | cached_result = result; |
224 | atomic_write_barrier (); |
225 | timestamp = now; |
226 | |
227 | return result; |
228 | } |
229 | libc_hidden_def (__get_nprocs) |
230 | weak_alias (__get_nprocs, get_nprocs) |
231 | |
232 | |
233 | /* On some architectures it is possible to distinguish between configured |
234 | and active cpus. */ |
235 | int |
236 | __get_nprocs_conf (void) |
237 | { |
238 | /* XXX Here will come a test for the new system call. */ |
239 | |
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 |
266 | /* If we haven't found an appropriate entry return 1. */ |
267 | FILE *fp = fopen ("/proc/cpuinfo" , "rce" ); |
268 | if (fp != NULL) |
269 | { |
270 | char buffer[8192]; |
271 | |
272 | /* No threads use this stream. */ |
273 | __fsetlocking (fp, FSETLOCKING_BYCALLER); |
274 | GET_NPROCS_CONF_PARSER (fp, buffer, result); |
275 | fclose (fp); |
276 | } |
277 | #else |
278 | result = __get_nprocs (); |
279 | #endif |
280 | |
281 | return result; |
282 | } |
283 | libc_hidden_def (__get_nprocs_conf) |
284 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
285 | |
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]. */ |
293 | static long int |
294 | sysinfo_mempages (unsigned long int num, unsigned int mem_unit) |
295 | { |
296 | unsigned long int ps = __getpagesize (); |
297 | |
298 | while (mem_unit > 1 && ps > 1) |
299 | { |
300 | mem_unit >>= 1; |
301 | ps >>= 1; |
302 | } |
303 | num *= mem_unit; |
304 | while (ps > 1) |
305 | { |
306 | ps >>= 1; |
307 | num >>= 1; |
308 | } |
309 | return num; |
310 | } |
311 | |
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. */ |
317 | long int |
318 | __get_phys_pages (void) |
319 | { |
320 | struct sysinfo info; |
321 | |
322 | __sysinfo (&info); |
323 | return sysinfo_mempages (info.totalram, info.mem_unit); |
324 | } |
325 | libc_hidden_def (__get_phys_pages) |
326 | weak_alias (__get_phys_pages, get_phys_pages) |
327 | |
328 | long int |
329 | __get_avphys_pages (void) |
330 | { |
331 | struct sysinfo info; |
332 | |
333 | __sysinfo (&info); |
334 | return sysinfo_mempages (info.freeram, info.mem_unit); |
335 | } |
336 | libc_hidden_def (__get_avphys_pages) |
337 | weak_alias (__get_avphys_pages, get_avphys_pages) |
338 | |