1 | /* Determine various system internal values, Linux version. |
2 | Copyright (C) 1996-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <array_length.h> |
20 | #include <assert.h> |
21 | #include <ctype.h> |
22 | #include <dirent.h> |
23 | #include <errno.h> |
24 | #include <ldsodefs.h> |
25 | #include <limits.h> |
26 | #include <not-cancel.h> |
27 | #include <stdio.h> |
28 | #include <stdio_ext.h> |
29 | #include <sys/mman.h> |
30 | #include <sys/sysinfo.h> |
31 | #include <sysdep.h> |
32 | |
33 | int |
34 | __get_nprocs_sched (void) |
35 | { |
36 | enum |
37 | { |
38 | max_num_cpus = 32768, |
39 | cpu_bits_size = CPU_ALLOC_SIZE (32768) |
40 | }; |
41 | |
42 | /* This cannot use malloc because it is used on malloc initialization. */ |
43 | __cpu_mask cpu_bits[cpu_bits_size / sizeof (__cpu_mask)]; |
44 | int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, cpu_bits_size, |
45 | cpu_bits); |
46 | if (r > 0) |
47 | return CPU_COUNT_S (cpu_bits_size, (cpu_set_t*) cpu_bits); |
48 | else if (r == -EINVAL) |
49 | /* The input buffer is still not enough to store the number of cpus. This |
50 | is an arbitrary values assuming such systems should be rare and there |
51 | is no offline cpus. */ |
52 | return max_num_cpus; |
53 | /* Some other error. 2 is conservative (not a uniprocessor system, so |
54 | atomics are needed). */ |
55 | return 2; |
56 | } |
57 | |
58 | static char * |
59 | next_line (int fd, char *const buffer, char **cp, char **re, |
60 | char *const buffer_end) |
61 | { |
62 | char *res = *cp; |
63 | char *nl = memchr (*cp, '\n', *re - *cp); |
64 | if (nl == NULL) |
65 | { |
66 | if (*cp != buffer) |
67 | { |
68 | if (*re == buffer_end) |
69 | { |
70 | memmove (buffer, *cp, *re - *cp); |
71 | *re = buffer + (*re - *cp); |
72 | *cp = buffer; |
73 | |
74 | ssize_t n = __read_nocancel (fd, *re, buffer_end - *re); |
75 | if (n < 0) |
76 | return NULL; |
77 | |
78 | *re += n; |
79 | |
80 | nl = memchr (*cp, '\n', *re - *cp); |
81 | while (nl == NULL && *re == buffer_end) |
82 | { |
83 | /* Truncate too long lines. */ |
84 | *re = buffer + 3 * (buffer_end - buffer) / 4; |
85 | n = __read_nocancel (fd, *re, buffer_end - *re); |
86 | if (n < 0) |
87 | return NULL; |
88 | |
89 | nl = memchr (*re, '\n', n); |
90 | **re = '\n'; |
91 | *re += n; |
92 | } |
93 | } |
94 | else |
95 | nl = memchr (*cp, '\n', *re - *cp); |
96 | |
97 | res = *cp; |
98 | } |
99 | |
100 | if (nl == NULL) |
101 | nl = *re - 1; |
102 | } |
103 | |
104 | *cp = nl + 1; |
105 | assert (*cp <= *re); |
106 | |
107 | return res == *re ? NULL : res; |
108 | } |
109 | |
110 | static int |
111 | get_nproc_stat (char *buffer, size_t buffer_size) |
112 | { |
113 | char *buffer_end = buffer + buffer_size; |
114 | char *cp = buffer_end; |
115 | char *re = buffer_end; |
116 | |
117 | /* Default to an SMP system in case we cannot obtain an accurate |
118 | number. */ |
119 | int result = 2; |
120 | |
121 | const int flags = O_RDONLY | O_CLOEXEC; |
122 | int fd = __open_nocancel ("/proc/stat" , flags); |
123 | if (fd != -1) |
124 | { |
125 | result = 0; |
126 | |
127 | char *l; |
128 | while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) |
129 | /* The current format of /proc/stat has all the cpu* entries |
130 | at the front. We assume here that stays this way. */ |
131 | if (strncmp (l, "cpu" , 3) != 0) |
132 | break; |
133 | else if (isdigit (l[3])) |
134 | ++result; |
135 | |
136 | __close_nocancel_nostatus (fd); |
137 | } |
138 | |
139 | return result; |
140 | } |
141 | |
142 | int |
143 | __get_nprocs (void) |
144 | { |
145 | enum { buffer_size = 1024 }; |
146 | char buffer[buffer_size]; |
147 | char *buffer_end = buffer + buffer_size; |
148 | char *cp = buffer_end; |
149 | char *re = buffer_end; |
150 | |
151 | const int flags = O_RDONLY | O_CLOEXEC; |
152 | /* This file contains comma-separated ranges. */ |
153 | int fd = __open_nocancel ("/sys/devices/system/cpu/online" , flags); |
154 | char *l; |
155 | int result = 0; |
156 | if (fd != -1) |
157 | { |
158 | l = next_line (fd, buffer, &cp, &re, buffer_end); |
159 | if (l != NULL) |
160 | do |
161 | { |
162 | char *endp; |
163 | unsigned long int n = strtoul (l, &endp, 10); |
164 | if (l == endp) |
165 | { |
166 | result = 0; |
167 | break; |
168 | } |
169 | |
170 | unsigned long int m = n; |
171 | if (*endp == '-') |
172 | { |
173 | l = endp + 1; |
174 | m = strtoul (l, &endp, 10); |
175 | if (l == endp) |
176 | { |
177 | result = 0; |
178 | break; |
179 | } |
180 | } |
181 | |
182 | result += m - n + 1; |
183 | |
184 | l = endp; |
185 | if (l < re && *l == ',') |
186 | ++l; |
187 | } |
188 | while (l < re && *l != '\n'); |
189 | |
190 | __close_nocancel_nostatus (fd); |
191 | |
192 | if (result > 0) |
193 | return result; |
194 | } |
195 | |
196 | return get_nproc_stat (buffer, buffer_size); |
197 | } |
198 | libc_hidden_def (__get_nprocs) |
199 | weak_alias (__get_nprocs, get_nprocs) |
200 | |
201 | |
202 | /* On some architectures it is possible to distinguish between configured |
203 | and active cpus. */ |
204 | int |
205 | __get_nprocs_conf (void) |
206 | { |
207 | /* Try to use the sysfs filesystem. It has actual information about |
208 | online processors. */ |
209 | DIR *dir = __opendir ("/sys/devices/system/cpu" ); |
210 | if (dir != NULL) |
211 | { |
212 | int count = 0; |
213 | struct dirent64 *d; |
214 | |
215 | while ((d = __readdir64 (dir)) != NULL) |
216 | /* NB: the sysfs has d_type support. */ |
217 | if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu" , 3) == 0) |
218 | { |
219 | char *endp; |
220 | unsigned long int nr = strtoul (d->d_name + 3, &endp, 10); |
221 | if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0') |
222 | ++count; |
223 | } |
224 | |
225 | __closedir (dir); |
226 | |
227 | return count; |
228 | } |
229 | |
230 | enum { buffer_size = 1024 }; |
231 | char buffer[buffer_size]; |
232 | return get_nproc_stat (buffer, buffer_size); |
233 | } |
234 | libc_hidden_def (__get_nprocs_conf) |
235 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
236 | |
237 | |
238 | /* Compute (num*mem_unit)/pagesize, but avoid overflowing long int. |
239 | In practice, mem_unit is never bigger than the page size, so after |
240 | the first loop it is 1. [In the kernel, it is initialized to |
241 | PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in |
242 | kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can |
243 | represent all the sizes measured in bytes]. */ |
244 | static long int |
245 | sysinfo_mempages (unsigned long int num, unsigned int mem_unit) |
246 | { |
247 | unsigned long int ps = __getpagesize (); |
248 | |
249 | while (mem_unit > 1 && ps > 1) |
250 | { |
251 | mem_unit >>= 1; |
252 | ps >>= 1; |
253 | } |
254 | num *= mem_unit; |
255 | while (ps > 1) |
256 | { |
257 | ps >>= 1; |
258 | num >>= 1; |
259 | } |
260 | return num; |
261 | } |
262 | |
263 | /* Return the number of pages of total/available physical memory in |
264 | the system. This used to be done by parsing /proc/meminfo, but |
265 | that's unnecessarily expensive (and /proc is not always available). |
266 | The sysinfo syscall provides the same information, and has been |
267 | available at least since kernel 2.3.48. */ |
268 | long int |
269 | __get_phys_pages (void) |
270 | { |
271 | struct sysinfo info; |
272 | |
273 | __sysinfo (&info); |
274 | return sysinfo_mempages (info.totalram, info.mem_unit); |
275 | } |
276 | libc_hidden_def (__get_phys_pages) |
277 | weak_alias (__get_phys_pages, get_phys_pages) |
278 | |
279 | long int |
280 | __get_avphys_pages (void) |
281 | { |
282 | struct sysinfo info; |
283 | |
284 | __sysinfo (&info); |
285 | return sysinfo_mempages (info.freeram, info.mem_unit); |
286 | } |
287 | libc_hidden_def (__get_avphys_pages) |
288 | weak_alias (__get_avphys_pages, get_avphys_pages) |
289 | |