1/* Determine various system internal values, Linux version.
2 Copyright (C) 1996-2021 Free Software Foundation, Inc.
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
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.
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
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20#include <array_length.h>
21#include <dirent.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/* Compute the population count of the entire array. */
33static int
34__get_nprocs_count (const unsigned long int *array, size_t length)
35{
36 int count = 0;
37 for (size_t i = 0; i < length; ++i)
38 if (__builtin_add_overflow (count, __builtin_popcountl (array[i]),
39 &count))
40 return INT_MAX;
41 return count;
42}
43
44/* __get_nprocs with a large buffer. */
45static int
46__get_nprocs_large (void)
47{
48 /* This code cannot use scratch_buffer because it is used during
49 malloc initialization. */
50 size_t pagesize = GLRO (dl_pagesize);
51 unsigned long int *page = __mmap (0, pagesize, PROT_READ | PROT_WRITE,
52 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
53 if (page == MAP_FAILED)
54 return 2;
55 int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, pagesize, page);
56 int count;
57 if (r > 0)
58 count = __get_nprocs_count (page, pagesize / sizeof (unsigned long int));
59 else if (r == -EINVAL)
60 /* One page is still not enough to store the bits. A more-or-less
61 arbitrary value. This assumes t hat such large systems never
62 happen in practice. */
63 count = GLRO (dl_pagesize) * CHAR_BIT;
64 else
65 count = 2;
66 __munmap (page, GLRO (dl_pagesize));
67 return count;
68}
69
70int
71__get_nprocs (void)
72{
73 /* Fast path for most systems. The kernel expects a buffer size
74 that is a multiple of 8. */
75 unsigned long int small_buffer[1024 / CHAR_BIT / sizeof (unsigned long int)];
76 int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0,
77 sizeof (small_buffer), small_buffer);
78 if (r > 0)
79 return __get_nprocs_count (small_buffer, r / sizeof (unsigned long int));
80 else if (r == -EINVAL)
81 /* The kernel requests a larger buffer to store the data. */
82 return __get_nprocs_large ();
83 else
84 /* Some other error. 2 is conservative (not a uniprocessor
85 system, so atomics are needed). */
86 return 2;
87}
88libc_hidden_def (__get_nprocs)
89weak_alias (__get_nprocs, get_nprocs)
90
91
92/* On some architectures it is possible to distinguish between configured
93 and active cpus. */
94int
95__get_nprocs_conf (void)
96{
97 /* Try to use the sysfs filesystem. It has actual information about
98 online processors. */
99 DIR *dir = __opendir ("/sys/devices/system/cpu");
100 if (dir != NULL)
101 {
102 int count = 0;
103 struct dirent64 *d;
104
105 while ((d = __readdir64 (dir)) != NULL)
106 /* NB: the sysfs has d_type support. */
107 if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu", 3) == 0)
108 {
109 char *endp;
110 unsigned long int nr = strtoul (d->d_name + 3, &endp, 10);
111 if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0')
112 ++count;
113 }
114
115 __closedir (dir);
116
117 return count;
118 }
119
120 return 1;
121}
122libc_hidden_def (__get_nprocs_conf)
123weak_alias (__get_nprocs_conf, get_nprocs_conf)
124
125
126/* Compute (num*mem_unit)/pagesize, but avoid overflowing long int.
127 In practice, mem_unit is never bigger than the page size, so after
128 the first loop it is 1. [In the kernel, it is initialized to
129 PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in
130 kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can
131 represent all the sizes measured in bytes]. */
132static long int
133sysinfo_mempages (unsigned long int num, unsigned int mem_unit)
134{
135 unsigned long int ps = __getpagesize ();
136
137 while (mem_unit > 1 && ps > 1)
138 {
139 mem_unit >>= 1;
140 ps >>= 1;
141 }
142 num *= mem_unit;
143 while (ps > 1)
144 {
145 ps >>= 1;
146 num >>= 1;
147 }
148 return num;
149}
150
151/* Return the number of pages of total/available physical memory in
152 the system. This used to be done by parsing /proc/meminfo, but
153 that's unnecessarily expensive (and /proc is not always available).
154 The sysinfo syscall provides the same information, and has been
155 available at least since kernel 2.3.48. */
156long int
157__get_phys_pages (void)
158{
159 struct sysinfo info;
160
161 __sysinfo (&info);
162 return sysinfo_mempages (info.totalram, info.mem_unit);
163}
164libc_hidden_def (__get_phys_pages)
165weak_alias (__get_phys_pages, get_phys_pages)
166
167long int
168__get_avphys_pages (void)
169{
170 struct sysinfo info;
171
172 __sysinfo (&info);
173 return sysinfo_mempages (info.freeram, info.mem_unit);
174}
175libc_hidden_def (__get_avphys_pages)
176weak_alias (__get_avphys_pages, get_avphys_pages)
177