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