]>
Commit | Line | Data |
---|---|---|
dfd2257a | 1 | /* Determine various system internal values, Linux version. |
2b778ceb | 2 | Copyright (C) 1996-2021 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 | 17 | License along with the GNU C Library; if not, see |
5a82c748 | 18 | <https://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 | ||
f9a75540 | 131 | time_t now = time_now (); |
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; |
b5eeca8c | 146 | /* This file contains comma-separated ranges. */ |
c2284574 | 147 | int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags); |
84e2a551 UD |
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; | |
b5eeca8c | 179 | if (l < re && *l == ',') |
84e2a551 UD |
180 | ++l; |
181 | } | |
b5eeca8c | 182 | while (l < re && *l != '\n'); |
84e2a551 | 183 | |
c181840c | 184 | __close_nocancel_nostatus (fd); |
84e2a551 UD |
185 | |
186 | if (result > 0) | |
187 | goto out; | |
188 | } | |
189 | ||
190 | cp = buffer_end; | |
191 | re = buffer_end; | |
1ff6c67a FW |
192 | |
193 | /* Default to an SMP system in case we cannot obtain an accurate | |
194 | number. */ | |
195 | result = 2; | |
84e2a551 | 196 | |
9506149a | 197 | /* The /proc/stat format is more uniform, use it by default. */ |
c2284574 | 198 | fd = __open_nocancel ("/proc/stat", flags); |
f43b4be6 | 199 | if (fd != -1) |
9506149a | 200 | { |
9506149a | 201 | result = 0; |
f43b4be6 | 202 | |
f43b4be6 | 203 | while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) |
6a3d03ff UD |
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])) | |
9506149a UD |
209 | ++result; |
210 | ||
c181840c | 211 | __close_nocancel_nostatus (fd); |
9506149a UD |
212 | } |
213 | else | |
214 | { | |
c2284574 | 215 | fd = __open_nocancel ("/proc/cpuinfo", flags); |
f43b4be6 | 216 | if (fd != -1) |
9506149a | 217 | { |
f43b4be6 | 218 | GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); |
c181840c | 219 | __close_nocancel_nostatus (fd); |
9506149a UD |
220 | } |
221 | } | |
222 | ||
84e2a551 | 223 | out: |
852eb34d UD |
224 | cached_result = result; |
225 | atomic_write_barrier (); | |
d53a73ac | 226 | timestamp = now; |
852eb34d | 227 | |
9506149a UD |
228 | return result; |
229 | } | |
b5648377 | 230 | libc_hidden_def (__get_nprocs) |
845dcb57 UD |
231 | weak_alias (__get_nprocs, get_nprocs) |
232 | ||
bdb04ee8 | 233 | |
bdb04ee8 UD |
234 | /* On some architectures it is possible to distinguish between configured |
235 | and active cpus. */ | |
236 | int | |
60d2f8f3 | 237 | __get_nprocs_conf (void) |
bdb04ee8 | 238 | { |
bdb04ee8 UD |
239 | /* XXX Here will come a test for the new system call. */ |
240 | ||
9506149a UD |
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 | |
bdb04ee8 | 267 | /* If we haven't found an appropriate entry return 1. */ |
f43b4be6 | 268 | FILE *fp = fopen ("/proc/cpuinfo", "rce"); |
5c980df9 | 269 | if (fp != NULL) |
bdb04ee8 | 270 | { |
9506149a UD |
271 | char buffer[8192]; |
272 | ||
5c980df9 UD |
273 | /* No threads use this stream. */ |
274 | __fsetlocking (fp, FSETLOCKING_BYCALLER); | |
275 | GET_NPROCS_CONF_PARSER (fp, buffer, result); | |
276 | fclose (fp); | |
bdb04ee8 | 277 | } |
9506149a | 278 | #else |
0abf17de | 279 | result = __get_nprocs (); |
9506149a | 280 | #endif |
bdb04ee8 UD |
281 | |
282 | return result; | |
283 | } | |
b5648377 | 284 | libc_hidden_def (__get_nprocs_conf) |
bdb04ee8 | 285 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
845dcb57 | 286 | |
0ce657c5 RV |
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]. */ | |
e4cf5229 | 294 | static long int |
0ce657c5 | 295 | sysinfo_mempages (unsigned long int num, unsigned int mem_unit) |
845dcb57 | 296 | { |
0ce657c5 | 297 | unsigned long int ps = __getpagesize (); |
845dcb57 | 298 | |
0ce657c5 | 299 | while (mem_unit > 1 && ps > 1) |
845dcb57 | 300 | { |
0ce657c5 RV |
301 | mem_unit >>= 1; |
302 | ps >>= 1; | |
845dcb57 | 303 | } |
0ce657c5 RV |
304 | num *= mem_unit; |
305 | while (ps > 1) | |
306 | { | |
307 | ps >>= 1; | |
308 | num >>= 1; | |
309 | } | |
310 | return num; | |
845dcb57 UD |
311 | } |
312 | ||
0ce657c5 RV |
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. */ | |
e4cf5229 | 318 | long int |
60d2f8f3 | 319 | __get_phys_pages (void) |
845dcb57 | 320 | { |
0ce657c5 | 321 | struct sysinfo info; |
845dcb57 | 322 | |
0ce657c5 RV |
323 | __sysinfo (&info); |
324 | return sysinfo_mempages (info.totalram, info.mem_unit); | |
845dcb57 | 325 | } |
b5648377 | 326 | libc_hidden_def (__get_phys_pages) |
845dcb57 UD |
327 | weak_alias (__get_phys_pages, get_phys_pages) |
328 | ||
e4cf5229 | 329 | long int |
60d2f8f3 | 330 | __get_avphys_pages (void) |
845dcb57 | 331 | { |
0ce657c5 | 332 | struct sysinfo info; |
845dcb57 | 333 | |
0ce657c5 RV |
334 | __sysinfo (&info); |
335 | return sysinfo_mempages (info.freeram, info.mem_unit); | |
845dcb57 | 336 | } |
b5648377 | 337 | libc_hidden_def (__get_avphys_pages) |
845dcb57 | 338 | weak_alias (__get_avphys_pages, get_avphys_pages) |