1 /* Determine various system internal values, Linux version.
2    Copyright (C) 1996-2022 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
__get_nprocs_sched(void)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 *
next_line(int fd,char * const buffer,char ** cp,char ** re,char * const buffer_end)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
get_nproc_stat(void)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
read_sysfs_file(const char * fname)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
get_nprocs_fallback(void)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
__get_nprocs(void)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)
weak_alias(__get_nprocs,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)
weak_alias(__get_nprocs_conf,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
__get_phys_pages(void)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)
weak_alias(__get_phys_pages,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