]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/unix/sysv/linux/getsysstats.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / sysdeps / unix / sysv / linux / getsysstats.c
CommitLineData
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
72static char *
73next_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 125int
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 230libc_hidden_def (__get_nprocs)
845dcb57
UD
231weak_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. */
236int
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 284libc_hidden_def (__get_nprocs_conf)
bdb04ee8 285weak_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 294static long int
0ce657c5 295sysinfo_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 318long 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 326libc_hidden_def (__get_phys_pages)
845dcb57
UD
327weak_alias (__get_phys_pages, get_phys_pages)
328
e4cf5229 329long 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 337libc_hidden_def (__get_avphys_pages)
845dcb57 338weak_alias (__get_avphys_pages, get_avphys_pages)