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