]>
Commit | Line | Data |
---|---|---|
dfd2257a | 1 | /* Determine various system internal values, Linux version. |
dff8da6b | 2 | Copyright (C) 1996-2024 Free Software Foundation, Inc. |
84384f5b | 3 | This file is part of the GNU C Library. |
84384f5b UD |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
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. | |
84384f5b UD |
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 | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
84384f5b | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 16 | License along with the GNU C Library; if not, see |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
84384f5b | 18 | |
eb68d7d2 | 19 | #include <array_length.h> |
34229827 AZ |
20 | #include <assert.h> |
21 | #include <ctype.h> | |
eb68d7d2 FW |
22 | #include <errno.h> |
23 | #include <ldsodefs.h> | |
24 | #include <limits.h> | |
903bc7dc | 25 | #include <not-cancel.h> |
845dcb57 | 26 | #include <stdio.h> |
2706ee38 | 27 | #include <stdio_ext.h> |
eb68d7d2 | 28 | #include <sys/mman.h> |
845dcb57 | 29 | #include <sys/sysinfo.h> |
903bc7dc | 30 | #include <sysdep.h> |
f43b4be6 | 31 | |
472894d2 | 32 | static int |
34229827 | 33 | __get_nprocs_sched (void) |
eb68d7d2 | 34 | { |
33099d72 AZ |
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); | |
eb68d7d2 | 45 | if (r > 0) |
97ba273b | 46 | return CPU_COUNT_S (r, (cpu_set_t*) cpu_bits); |
eb68d7d2 | 47 | else if (r == -EINVAL) |
33099d72 AZ |
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; | |
e1d32b83 DL |
52 | /* Some other error. */ |
53 | return 0; | |
9506149a | 54 | } |
34229827 AZ |
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 | ||
137ed5ac | 108 | static int |
e1d32b83 | 109 | get_nproc_stat (void) |
137ed5ac | 110 | { |
e1d32b83 DL |
111 | enum { buffer_size = 1024 }; |
112 | char buffer[buffer_size]; | |
137ed5ac AZ |
113 | char *buffer_end = buffer + buffer_size; |
114 | char *cp = buffer_end; | |
115 | char *re = buffer_end; | |
e1d32b83 | 116 | int result = 0; |
137ed5ac AZ |
117 | |
118 | const int flags = O_RDONLY | O_CLOEXEC; | |
119 | int fd = __open_nocancel ("/proc/stat", flags); | |
120 | if (fd != -1) | |
121 | { | |
137ed5ac AZ |
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 | } | |
845dcb57 | 136 | |
e1d32b83 | 137 | static int |
97a912f7 | 138 | read_sysfs_file (const char *fname) |
11a02b03 | 139 | { |
34229827 AZ |
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. */ | |
97a912f7 | 148 | int fd = __open_nocancel (fname, flags); |
34229827 AZ |
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 | ||
e1d32b83 DL |
177 | if (m >= n) |
178 | result += m - n + 1; | |
34229827 AZ |
179 | |
180 | l = endp; | |
181 | if (l < re && *l == ',') | |
182 | ++l; | |
183 | } | |
184 | while (l < re && *l != '\n'); | |
185 | ||
186 | __close_nocancel_nostatus (fd); | |
34229827 AZ |
187 | } |
188 | ||
e1d32b83 | 189 | return result; |
11a02b03 | 190 | } |
bdb04ee8 | 191 | |
e1d32b83 DL |
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 | { | |
97a912f7 | 216 | int result = read_sysfs_file ("/sys/devices/system/cpu/online"); |
e1d32b83 DL |
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 | { | |
97a912f7 | 231 | int result = read_sysfs_file ("/sys/devices/system/cpu/possible"); |
e1d32b83 DL |
232 | if (result != 0) |
233 | return result; | |
234 | ||
235 | /* Fall back to /proc/stat and sched_getaffinity. */ | |
236 | return get_nprocs_fallback (); | |
bdb04ee8 | 237 | } |
b5648377 | 238 | libc_hidden_def (__get_nprocs_conf) |
bdb04ee8 | 239 | weak_alias (__get_nprocs_conf, get_nprocs_conf) |
845dcb57 | 240 | |
0ce657c5 RV |
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]. */ | |
e4cf5229 | 248 | static long int |
0ce657c5 | 249 | sysinfo_mempages (unsigned long int num, unsigned int mem_unit) |
845dcb57 | 250 | { |
0ce657c5 | 251 | unsigned long int ps = __getpagesize (); |
845dcb57 | 252 | |
0ce657c5 | 253 | while (mem_unit > 1 && ps > 1) |
845dcb57 | 254 | { |
0ce657c5 RV |
255 | mem_unit >>= 1; |
256 | ps >>= 1; | |
845dcb57 | 257 | } |
0ce657c5 RV |
258 | num *= mem_unit; |
259 | while (ps > 1) | |
260 | { | |
261 | ps >>= 1; | |
262 | num >>= 1; | |
263 | } | |
264 | return num; | |
845dcb57 UD |
265 | } |
266 | ||
0ce657c5 RV |
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. */ | |
e4cf5229 | 272 | long int |
60d2f8f3 | 273 | __get_phys_pages (void) |
845dcb57 | 274 | { |
0ce657c5 | 275 | struct sysinfo info; |
845dcb57 | 276 | |
0ce657c5 RV |
277 | __sysinfo (&info); |
278 | return sysinfo_mempages (info.totalram, info.mem_unit); | |
845dcb57 | 279 | } |
b5648377 | 280 | libc_hidden_def (__get_phys_pages) |
845dcb57 UD |
281 | weak_alias (__get_phys_pages, get_phys_pages) |
282 | ||
e4cf5229 | 283 | long int |
60d2f8f3 | 284 | __get_avphys_pages (void) |
845dcb57 | 285 | { |
0ce657c5 | 286 | struct sysinfo info; |
845dcb57 | 287 | |
0ce657c5 RV |
288 | __sysinfo (&info); |
289 | return sysinfo_mempages (info.freeram, info.mem_unit); | |
845dcb57 | 290 | } |
b5648377 | 291 | libc_hidden_def (__get_avphys_pages) |
845dcb57 | 292 | weak_alias (__get_avphys_pages, get_avphys_pages) |