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 
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 <dirent.h>
23 #include <errno.h>
24 #include <ldsodefs.h>
25 #include <limits.h>
26 #include <not-cancel.h>
27 #include <stdio.h>
28 #include <stdio_ext.h>
29 #include <sys/mman.h>
30 #include <sys/sysinfo.h>
31 #include <sysdep.h>
32 
33 int
__get_nprocs_sched(void)34 __get_nprocs_sched (void)
35 {
36   enum
37     {
38       max_num_cpus = 32768,
39       cpu_bits_size = CPU_ALLOC_SIZE (32768)
40     };
41 
42   /* This cannot use malloc because it is used on malloc initialization.  */
43   __cpu_mask cpu_bits[cpu_bits_size / sizeof (__cpu_mask)];
44   int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, cpu_bits_size,
45 				 cpu_bits);
46   if (r > 0)
47     return CPU_COUNT_S (cpu_bits_size, (cpu_set_t*) cpu_bits);
48   else if (r == -EINVAL)
49     /* The input buffer is still not enough to store the number of cpus.  This
50        is an arbitrary values assuming such systems should be rare and there
51        is no offline cpus.  */
52     return max_num_cpus;
53   /* Some other error.  2 is conservative (not a uniprocessor system, so
54      atomics are needed). */
55   return 2;
56 }
57 
58 static char *
next_line(int fd,char * const buffer,char ** cp,char ** re,char * const buffer_end)59 next_line (int fd, char *const buffer, char **cp, char **re,
60            char *const buffer_end)
61 {
62   char *res = *cp;
63   char *nl = memchr (*cp, '\n', *re - *cp);
64   if (nl == NULL)
65     {
66       if (*cp != buffer)
67         {
68           if (*re == buffer_end)
69             {
70               memmove (buffer, *cp, *re - *cp);
71               *re = buffer + (*re - *cp);
72               *cp = buffer;
73 
74               ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
75               if (n < 0)
76                 return NULL;
77 
78               *re += n;
79 
80               nl = memchr (*cp, '\n', *re - *cp);
81               while (nl == NULL && *re == buffer_end)
82                 {
83                   /* Truncate too long lines.  */
84                   *re = buffer + 3 * (buffer_end - buffer) / 4;
85                   n = __read_nocancel (fd, *re, buffer_end - *re);
86                   if (n < 0)
87                     return NULL;
88 
89                   nl = memchr (*re, '\n', n);
90                   **re = '\n';
91                   *re += n;
92                 }
93             }
94           else
95             nl = memchr (*cp, '\n', *re - *cp);
96 
97           res = *cp;
98         }
99 
100       if (nl == NULL)
101         nl = *re - 1;
102     }
103 
104   *cp = nl + 1;
105   assert (*cp <= *re);
106 
107   return res == *re ? NULL : res;
108 }
109 
110 static int
get_nproc_stat(char * buffer,size_t buffer_size)111 get_nproc_stat (char *buffer, size_t buffer_size)
112 {
113   char *buffer_end = buffer + buffer_size;
114   char *cp = buffer_end;
115   char *re = buffer_end;
116 
117   /* Default to an SMP system in case we cannot obtain an accurate
118      number.  */
119   int result = 2;
120 
121   const int flags = O_RDONLY | O_CLOEXEC;
122   int fd = __open_nocancel ("/proc/stat", flags);
123   if (fd != -1)
124     {
125       result = 0;
126 
127       char *l;
128       while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL)
129 	/* The current format of /proc/stat has all the cpu* entries
130 	   at the front.  We assume here that stays this way.  */
131 	if (strncmp (l, "cpu", 3) != 0)
132 	  break;
133 	else if (isdigit (l[3]))
134 	  ++result;
135 
136       __close_nocancel_nostatus (fd);
137     }
138 
139   return result;
140 }
141 
142 int
__get_nprocs(void)143 __get_nprocs (void)
144 {
145   enum { buffer_size = 1024 };
146   char buffer[buffer_size];
147   char *buffer_end = buffer + buffer_size;
148   char *cp = buffer_end;
149   char *re = buffer_end;
150 
151   const int flags = O_RDONLY | O_CLOEXEC;
152   /* This file contains comma-separated ranges.  */
153   int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags);
154   char *l;
155   int result = 0;
156   if (fd != -1)
157     {
158       l = next_line (fd, buffer, &cp, &re, buffer_end);
159       if (l != NULL)
160 	do
161 	  {
162 	    char *endp;
163 	    unsigned long int n = strtoul (l, &endp, 10);
164 	    if (l == endp)
165 	      {
166 		result = 0;
167 		break;
168 	      }
169 
170 	    unsigned long int m = n;
171 	    if (*endp == '-')
172 	      {
173 		l = endp + 1;
174 		m = strtoul (l, &endp, 10);
175 		if (l == endp)
176 		  {
177 		    result = 0;
178 		    break;
179 		  }
180 	      }
181 
182 	    result += m - n + 1;
183 
184 	    l = endp;
185 	    if (l < re && *l == ',')
186 	      ++l;
187 	  }
188 	while (l < re && *l != '\n');
189 
190       __close_nocancel_nostatus (fd);
191 
192       if (result > 0)
193 	return result;
194     }
195 
196   return get_nproc_stat (buffer, buffer_size);
197 }
198 libc_hidden_def (__get_nprocs)
weak_alias(__get_nprocs,get_nprocs)199 weak_alias (__get_nprocs, get_nprocs)
200 
201 
202 /* On some architectures it is possible to distinguish between configured
203    and active cpus.  */
204 int
205 __get_nprocs_conf (void)
206 {
207   /* Try to use the sysfs filesystem.  It has actual information about
208      online processors.  */
209   DIR *dir = __opendir ("/sys/devices/system/cpu");
210   if (dir != NULL)
211     {
212       int count = 0;
213       struct dirent64 *d;
214 
215       while ((d = __readdir64 (dir)) != NULL)
216 	/* NB: the sysfs has d_type support.  */
217 	if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu", 3) == 0)
218 	  {
219 	    char *endp;
220 	    unsigned long int nr = strtoul (d->d_name + 3, &endp, 10);
221 	    if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0')
222 	      ++count;
223 	  }
224 
225       __closedir (dir);
226 
227       return count;
228     }
229 
230   enum { buffer_size = 1024 };
231   char buffer[buffer_size];
232   return get_nproc_stat (buffer, buffer_size);
233 }
234 libc_hidden_def (__get_nprocs_conf)
weak_alias(__get_nprocs_conf,get_nprocs_conf)235 weak_alias (__get_nprocs_conf, get_nprocs_conf)
236 
237 
238 /* Compute (num*mem_unit)/pagesize, but avoid overflowing long int.
239    In practice, mem_unit is never bigger than the page size, so after
240    the first loop it is 1.  [In the kernel, it is initialized to
241    PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in
242    kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can
243    represent all the sizes measured in bytes].  */
244 static long int
245 sysinfo_mempages (unsigned long int num, unsigned int mem_unit)
246 {
247   unsigned long int ps = __getpagesize ();
248 
249   while (mem_unit > 1 && ps > 1)
250     {
251       mem_unit >>= 1;
252       ps >>= 1;
253     }
254   num *= mem_unit;
255   while (ps > 1)
256     {
257       ps >>= 1;
258       num >>= 1;
259     }
260   return num;
261 }
262 
263 /* Return the number of pages of total/available physical memory in
264    the system.  This used to be done by parsing /proc/meminfo, but
265    that's unnecessarily expensive (and /proc is not always available).
266    The sysinfo syscall provides the same information, and has been
267    available at least since kernel 2.3.48.  */
268 long int
__get_phys_pages(void)269 __get_phys_pages (void)
270 {
271   struct sysinfo info;
272 
273   __sysinfo (&info);
274   return sysinfo_mempages (info.totalram, info.mem_unit);
275 }
276 libc_hidden_def (__get_phys_pages)
weak_alias(__get_phys_pages,get_phys_pages)277 weak_alias (__get_phys_pages, get_phys_pages)
278 
279 long int
280 __get_avphys_pages (void)
281 {
282   struct sysinfo info;
283 
284   __sysinfo (&info);
285   return sysinfo_mempages (info.freeram, info.mem_unit);
286 }
287 libc_hidden_def (__get_avphys_pages)
288 weak_alias (__get_avphys_pages, get_avphys_pages)
289