Blame sysdeps/unix/sysv/linux/getsysstats.c

Packit 6c4009
/* Determine various system internal values, Linux version.
Packit 6c4009
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <alloca.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <dirent.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <mntent.h>
Packit 6c4009
#include <paths.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdio_ext.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/sysinfo.h>
Packit 6c4009
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* How we can determine the number of available processors depends on
Packit 6c4009
   the configuration.  There is currently (as of version 2.0.21) no
Packit 6c4009
   system call to determine the number.  It is planned for the 2.1.x
Packit 6c4009
   series to add this, though.
Packit 6c4009
Packit 6c4009
   One possibility to implement it for systems using Linux 2.0 is to
Packit 6c4009
   examine the pseudo file /proc/cpuinfo.  Here we have one entry for
Packit 6c4009
   each processor.
Packit 6c4009
Packit 6c4009
   But not all systems have support for the /proc filesystem.  If it
Packit 6c4009
   is not available we simply return 1 since there is no way.  */
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Other architectures use different formats for /proc/cpuinfo.  This
Packit 6c4009
   provides a hook for alternative parsers.  */
Packit 6c4009
#ifndef GET_NPROCS_PARSER
Packit 6c4009
# define GET_NPROCS_PARSER(FD, BUFFER, CP, RE, BUFFER_END, RESULT) \
Packit 6c4009
  do									\
Packit 6c4009
    {									\
Packit 6c4009
      (RESULT) = 0;							\
Packit 6c4009
      /* Read all lines and count the lines starting with the string	\
Packit 6c4009
	 "processor".  We don't have to fear extremely long lines since	\
Packit 6c4009
	 the kernel will not generate them.  8192 bytes are really	\
Packit 6c4009
	 enough.  */							\
Packit 6c4009
      char *l;								\
Packit 6c4009
      while ((l = next_line (FD, BUFFER, &CP, &RE, BUFFER_END)) != NULL) \
Packit 6c4009
	if (strncmp (l, "processor", 9) == 0)				\
Packit 6c4009
	  ++(RESULT);							\
Packit 6c4009
    }									\
Packit 6c4009
  while (0)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
static char *
Packit 6c4009
next_line (int fd, char *const buffer, char **cp, char **re,
Packit 6c4009
	   char *const buffer_end)
Packit 6c4009
{
Packit 6c4009
  char *res = *cp;
Packit 6c4009
  char *nl = memchr (*cp, '\n', *re - *cp);
Packit 6c4009
  if (nl == NULL)
Packit 6c4009
    {
Packit 6c4009
      if (*cp != buffer)
Packit 6c4009
	{
Packit 6c4009
	  if (*re == buffer_end)
Packit 6c4009
	    {
Packit 6c4009
	      memmove (buffer, *cp, *re - *cp);
Packit 6c4009
	      *re = buffer + (*re - *cp);
Packit 6c4009
	      *cp = buffer;
Packit 6c4009
Packit 6c4009
	      ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
Packit 6c4009
	      if (n < 0)
Packit 6c4009
		return NULL;
Packit 6c4009
Packit 6c4009
	      *re += n;
Packit 6c4009
Packit 6c4009
	      nl = memchr (*cp, '\n', *re - *cp);
Packit 6c4009
	      while (nl == NULL && *re == buffer_end)
Packit 6c4009
		{
Packit 6c4009
		  /* Truncate too long lines.  */
Packit 6c4009
		  *re = buffer + 3 * (buffer_end - buffer) / 4;
Packit 6c4009
		  n = __read_nocancel (fd, *re, buffer_end - *re);
Packit 6c4009
		  if (n < 0)
Packit 6c4009
		    return NULL;
Packit 6c4009
Packit 6c4009
		  nl = memchr (*re, '\n', n);
Packit 6c4009
		  **re = '\n';
Packit 6c4009
		  *re += n;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    nl = memchr (*cp, '\n', *re - *cp);
Packit 6c4009
Packit 6c4009
	  res = *cp;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (nl == NULL)
Packit 6c4009
	nl = *re - 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  *cp = nl + 1;
Packit 6c4009
  assert (*cp <= *re);
Packit 6c4009
Packit 6c4009
  return res == *re ? NULL : res;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__get_nprocs (void)
Packit 6c4009
{
Packit 6c4009
  static int cached_result = -1;
Packit 6c4009
  static time_t timestamp;
Packit 6c4009
Packit 6c4009
  time_t now = time (NULL);
Packit 6c4009
  time_t prev = timestamp;
Packit 6c4009
  atomic_read_barrier ();
Packit 6c4009
  if (now == prev && cached_result > -1)
Packit 6c4009
    return cached_result;
Packit 6c4009
Packit 6c4009
  /* XXX Here will come a test for the new system call.  */
Packit 6c4009
Packit 6c4009
  const size_t buffer_size = __libc_use_alloca (8192) ? 8192 : 512;
Packit 6c4009
  char *buffer = alloca (buffer_size);
Packit 6c4009
  char *buffer_end = buffer + buffer_size;
Packit 6c4009
  char *cp = buffer_end;
Packit 6c4009
  char *re = buffer_end;
Packit 6c4009
Packit 6c4009
  const int flags = O_RDONLY | O_CLOEXEC;
Packit 6c4009
  int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags);
Packit 6c4009
  char *l;
Packit 6c4009
  int result = 0;
Packit 6c4009
  if (fd != -1)
Packit 6c4009
    {
Packit 6c4009
      l = next_line (fd, buffer, &cp, &re, buffer_end);
Packit 6c4009
      if (l != NULL)
Packit 6c4009
	do
Packit 6c4009
	  {
Packit 6c4009
	    char *endp;
Packit 6c4009
	    unsigned long int n = strtoul (l, &endp, 10);
Packit 6c4009
	    if (l == endp)
Packit 6c4009
	      {
Packit 6c4009
		result = 0;
Packit 6c4009
		break;
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    unsigned long int m = n;
Packit 6c4009
	    if (*endp == '-')
Packit 6c4009
	      {
Packit 6c4009
		l = endp + 1;
Packit 6c4009
		m = strtoul (l, &endp, 10);
Packit 6c4009
		if (l == endp)
Packit 6c4009
		  {
Packit 6c4009
		    result = 0;
Packit 6c4009
		    break;
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    result += m - n + 1;
Packit 6c4009
Packit 6c4009
	    l = endp;
Packit 6c4009
	    while (l < re && isspace (*l))
Packit 6c4009
	      ++l;
Packit 6c4009
	  }
Packit 6c4009
	while (l < re);
Packit 6c4009
Packit 6c4009
      __close_nocancel_nostatus (fd);
Packit 6c4009
Packit 6c4009
      if (result > 0)
Packit 6c4009
	goto out;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  cp = buffer_end;
Packit 6c4009
  re = buffer_end;
Packit 6c4009
Packit 6c4009
  /* Default to an SMP system in case we cannot obtain an accurate
Packit 6c4009
     number.  */
Packit 6c4009
  result = 2;
Packit 6c4009
Packit 6c4009
  /* The /proc/stat format is more uniform, use it by default.  */
Packit 6c4009
  fd = __open_nocancel ("/proc/stat", flags);
Packit 6c4009
  if (fd != -1)
Packit 6c4009
    {
Packit 6c4009
      result = 0;
Packit 6c4009
Packit 6c4009
      while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL)
Packit 6c4009
	/* The current format of /proc/stat has all the cpu* entries
Packit 6c4009
	   at the front.  We assume here that stays this way.  */
Packit 6c4009
	if (strncmp (l, "cpu", 3) != 0)
Packit 6c4009
	  break;
Packit 6c4009
	else if (isdigit (l[3]))
Packit 6c4009
	  ++result;
Packit 6c4009
Packit 6c4009
      __close_nocancel_nostatus (fd);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      fd = __open_nocancel ("/proc/cpuinfo", flags);
Packit 6c4009
      if (fd != -1)
Packit 6c4009
	{
Packit 6c4009
	  GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result);
Packit 6c4009
	  __close_nocancel_nostatus (fd);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  cached_result = result;
Packit 6c4009
  atomic_write_barrier ();
Packit 6c4009
  timestamp = now;
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__get_nprocs)
Packit 6c4009
weak_alias (__get_nprocs, get_nprocs)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* On some architectures it is possible to distinguish between configured
Packit 6c4009
   and active cpus.  */
Packit 6c4009
int
Packit 6c4009
__get_nprocs_conf (void)
Packit 6c4009
{
Packit 6c4009
  /* XXX Here will come a test for the new system call.  */
Packit 6c4009
Packit 6c4009
  /* Try to use the sysfs filesystem.  It has actual information about
Packit 6c4009
     online processors.  */
Packit 6c4009
  DIR *dir = __opendir ("/sys/devices/system/cpu");
Packit 6c4009
  if (dir != NULL)
Packit 6c4009
    {
Packit 6c4009
      int count = 0;
Packit 6c4009
      struct dirent64 *d;
Packit 6c4009
Packit 6c4009
      while ((d = __readdir64 (dir)) != NULL)
Packit 6c4009
	/* NB: the sysfs has d_type support.  */
Packit 6c4009
	if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu", 3) == 0)
Packit 6c4009
	  {
Packit 6c4009
	    char *endp;
Packit 6c4009
	    unsigned long int nr = strtoul (d->d_name + 3, &endp, 10);
Packit 6c4009
	    if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0')
Packit 6c4009
	      ++count;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
      __closedir (dir);
Packit 6c4009
Packit 6c4009
      return count;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  int result = 1;
Packit 6c4009
Packit 6c4009
#ifdef GET_NPROCS_CONF_PARSER
Packit 6c4009
  /* If we haven't found an appropriate entry return 1.  */
Packit 6c4009
  FILE *fp = fopen ("/proc/cpuinfo", "rce");
Packit 6c4009
  if (fp != NULL)
Packit 6c4009
    {
Packit 6c4009
      char buffer[8192];
Packit 6c4009
Packit 6c4009
      /* No threads use this stream.  */
Packit 6c4009
      __fsetlocking (fp, FSETLOCKING_BYCALLER);
Packit 6c4009
      GET_NPROCS_CONF_PARSER (fp, buffer, result);
Packit 6c4009
      fclose (fp);
Packit 6c4009
    }
Packit 6c4009
#else
Packit 6c4009
  result = __get_nprocs ();
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__get_nprocs_conf)
Packit 6c4009
weak_alias (__get_nprocs_conf, get_nprocs_conf)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Compute (num*mem_unit)/pagesize, but avoid overflowing long int.
Packit 6c4009
   In practice, mem_unit is never bigger than the page size, so after
Packit 6c4009
   the first loop it is 1.  [In the kernel, it is initialized to
Packit 6c4009
   PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in
Packit 6c4009
   kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can
Packit 6c4009
   represent all the sizes measured in bytes].  */
Packit 6c4009
static long int
Packit 6c4009
sysinfo_mempages (unsigned long int num, unsigned int mem_unit)
Packit 6c4009
{
Packit 6c4009
  unsigned long int ps = __getpagesize ();
Packit 6c4009
Packit 6c4009
  while (mem_unit > 1 && ps > 1)
Packit 6c4009
    {
Packit 6c4009
      mem_unit >>= 1;
Packit 6c4009
      ps >>= 1;
Packit 6c4009
    }
Packit 6c4009
  num *= mem_unit;
Packit 6c4009
  while (ps > 1)
Packit 6c4009
    {
Packit 6c4009
      ps >>= 1;
Packit 6c4009
      num >>= 1;
Packit 6c4009
    }
Packit 6c4009
  return num;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return the number of pages of total/available physical memory in
Packit 6c4009
   the system.  This used to be done by parsing /proc/meminfo, but
Packit 6c4009
   that's unnecessarily expensive (and /proc is not always available).
Packit 6c4009
   The sysinfo syscall provides the same information, and has been
Packit 6c4009
   available at least since kernel 2.3.48.  */
Packit 6c4009
long int
Packit 6c4009
__get_phys_pages (void)
Packit 6c4009
{
Packit 6c4009
  struct sysinfo info;
Packit 6c4009
Packit 6c4009
  __sysinfo (&info;;
Packit 6c4009
  return sysinfo_mempages (info.totalram, info.mem_unit);
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__get_phys_pages)
Packit 6c4009
weak_alias (__get_phys_pages, get_phys_pages)
Packit 6c4009
Packit 6c4009
long int
Packit 6c4009
__get_avphys_pages (void)
Packit 6c4009
{
Packit 6c4009
  struct sysinfo info;
Packit 6c4009
Packit 6c4009
  __sysinfo (&info;;
Packit 6c4009
  return sysinfo_mempages (info.freeram, info.mem_unit);
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__get_avphys_pages)
Packit 6c4009
weak_alias (__get_avphys_pages, get_avphys_pages)