mips: FIx clone3 implementation (BZ 31325)
[glibc.git] / sysdeps / unix / sysv / linux / getsysstats.c
blob1c041b7ebb00f4165c66841f995715dfe2dad77b
1 /* Determine various system internal values, Linux version.
2 Copyright (C) 1996-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
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>
32 static int
33 __get_nprocs_sched (void)
35 enum
37 max_num_cpus = 32768,
38 cpu_bits_size = CPU_ALLOC_SIZE (32768)
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;
56 static char *
57 next_line (int fd, char *const buffer, char **cp, char **re,
58 char *const buffer_end)
60 char *res = *cp;
61 char *nl = memchr (*cp, '\n', *re - *cp);
62 if (nl == NULL)
64 if (*cp != buffer)
66 if (*re == buffer_end)
68 memmove (buffer, *cp, *re - *cp);
69 *re = buffer + (*re - *cp);
70 *cp = buffer;
72 ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
73 if (n < 0)
74 return NULL;
76 *re += n;
78 nl = memchr (*cp, '\n', *re - *cp);
79 while (nl == NULL && *re == buffer_end)
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;
87 nl = memchr (*re, '\n', n);
88 **re = '\n';
89 *re += n;
92 else
93 nl = memchr (*cp, '\n', *re - *cp);
95 res = *cp;
98 if (nl == NULL)
99 nl = *re - 1;
102 *cp = nl + 1;
103 assert (*cp <= *re);
105 return res == *re ? NULL : res;
108 static int
109 get_nproc_stat (void)
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;
118 const int flags = O_RDONLY | O_CLOEXEC;
119 int fd = __open_nocancel ("/proc/stat", flags);
120 if (fd != -1)
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;
131 __close_nocancel_nostatus (fd);
134 return result;
137 static int
138 read_sysfs_file (const char *fname)
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;
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)
153 l = next_line (fd, buffer, &cp, &re, buffer_end);
154 if (l != NULL)
157 char *endp;
158 unsigned long int n = strtoul (l, &endp, 10);
159 if (l == endp)
161 result = 0;
162 break;
165 unsigned long int m = n;
166 if (*endp == '-')
168 l = endp + 1;
169 m = strtoul (l, &endp, 10);
170 if (l == endp)
172 result = 0;
173 break;
177 if (m >= n)
178 result += m - n + 1;
180 l = endp;
181 if (l < re && *l == ',')
182 ++l;
184 while (l < re && *l != '\n');
186 __close_nocancel_nostatus (fd);
189 return result;
192 static int
193 get_nprocs_fallback (void)
195 int result;
197 /* Try /proc/stat first. */
198 result = get_nproc_stat ();
199 if (result != 0)
200 return result;
202 /* Try sched_getaffinity. */
203 result = __get_nprocs_sched ();
204 if (result != 0)
205 return result;
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;
214 __get_nprocs (void)
216 int result = read_sysfs_file ("/sys/devices/system/cpu/online");
217 if (result != 0)
218 return result;
220 /* Fall back to /proc/stat and sched_getaffinity. */
221 return get_nprocs_fallback ();
223 libc_hidden_def (__get_nprocs)
224 weak_alias (__get_nprocs, get_nprocs)
226 /* On some architectures it is possible to distinguish between configured
227 and active cpus. */
229 __get_nprocs_conf (void)
231 int result = read_sysfs_file ("/sys/devices/system/cpu/possible");
232 if (result != 0)
233 return result;
235 /* Fall back to /proc/stat and sched_getaffinity. */
236 return get_nprocs_fallback ();
238 libc_hidden_def (__get_nprocs_conf)
239 weak_alias (__get_nprocs_conf, get_nprocs_conf)
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)
251 unsigned long int ps = __getpagesize ();
253 while (mem_unit > 1 && ps > 1)
255 mem_unit >>= 1;
256 ps >>= 1;
258 num *= mem_unit;
259 while (ps > 1)
261 ps >>= 1;
262 num >>= 1;
264 return num;
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)
275 struct sysinfo info;
277 __sysinfo (&info);
278 return sysinfo_mempages (info.totalram, info.mem_unit);
280 libc_hidden_def (__get_phys_pages)
281 weak_alias (__get_phys_pages, get_phys_pages)
283 long int
284 __get_avphys_pages (void)
286 struct sysinfo info;
288 __sysinfo (&info);
289 return sysinfo_mempages (info.freeram, info.mem_unit);
291 libc_hidden_def (__get_avphys_pages)
292 weak_alias (__get_avphys_pages, get_avphys_pages)