Re: libcruft/sysconf_cpus.c patch

Bela Lubkin <[email protected]> Tue, 23 Nov 2010 17:58:05 -0800
Newsgroups gmane.linux.lib.dietlibc
Message-ID <[email protected]>
Indan Zupancic wrote:

> (Bela forgot to CC the list.)

Actually I completely missed my aim, intended to send only to the list
and you'd get your list copy.

> > It'll also be wrong if you were exec'd by someone who used
> > sched_setaffinity() to restrict your horizons.
>
> Not really. The manpage says:
>
>         - _SC_NPROCESSORS_CONF
>               The number of processors configured.
>
>         - _SC_NPROCESSORS_ONLN
>               The number of processors currently online (available).
>
> I'd argue that for ONLN returning the number of available cpus is the
> correct thing to do.

Hmmm.  _SC_NPROCESSORS_ONLN is a non-POSIX extension.  glibc doc at
<http://www.gnu.org/s/libc/manual/html_node/Processor-Resources.html>
says:

" But it might be possible for the operating system to disable individual
" processors and so the call
"
"        sysconf (_SC_NPROCESSORS_ONLN)
"
" returns the number of processors which are currently inline (i.e.,
" available).

There are at least 3 numbers one *could* want to know: (1) # CPUs in the
hardware, (2) # CPUs activated by the system (e.g. if the OS has CPU
licenses, or detected some are broken), and (3) # CPUs *this* process is
allowed to access.  The doc says (to me) #2.

Anyway, the number returned should be the same as glibc on the same
platform.  FWIW, glibc counts "cpu%d" subdirectories in
/sys/devices/system/cpu for _SC_NPROCESSORS_CONF, and "^cpu%d" lines in
/proc/stat for _SC_NPROCESSORS_ONLN.  Is /proc/stat format at least as
stable as (not-very-stable) /proc/cpuinfo across Linux arches &
releases?  Nothing in dietlibc uses /sys/... yet; I don't think that's a
good bridge to cross even though current Linux conveniently provides
/sys/devices/system/cpu/present (#1) and online (#2).  Added in 2008-12
according to linux/Documentation/ABI/testing/sysfs-devices-system-cpu
and Documentationlinux//cputopology.txt.

> Anyway, if you don't want that, and also want to support CONF you can
> save the original mask, then figure out the number of cpus with
> sched_setaffinity(), before restoring the original. Though that is
> more bother than really worth it.

You're assuming the kernel get/set implementation will clear bits above
the actual # CPUs in the system.  It could just as reasonably let you
set as many bits as you want, knowing the scheduler will only pay
attention to the ones that count.

> > Anyway, I haven't noticed sched_[gs]etaffinity() support in dietlibc!
>
> The kernel supports it, it's a system call, so adding it to dietlibc
> is trivial.

The stuff in glibc is uhhhgly, feel free...

> > Writing a fully working /proc version isn't hard... stand by.

I came up with this.  It compiles to 51 bytes more than the old one for
me (gcc 4.4.5/x86 32-bit), but that's the price for working 100%.  Also,
it assumes strlen("constant string") is evaluated at compile time -- I'm
not sure if that's true for all compilers dietlibc might see.  If not,
NR_CPUS_LEN should be defined in each lobe of that #ifdef.

Retained existing style; changed `i' -> `idx', `n' -> `nb' for better
greppability; tested with smaller buffers to make sure the algorithm was
stable.  Changed (buf[m]=='x' && memcmp(buf+m,"xyz",n)) to just memcmp()
since it's only one per input line and memcmp() returns on 1st mismatch
anyway.

So, new libcruft/sysconf_cpus.c (and it should definitely be mentioned
in PORTING):

========================================================================
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include "dietfeatures.h"

/*
 * by Olaf Dreesen
 * rewritten by Bela Lubkin to handle buffer wrap
 *
 * arm			NO SMP ?!? (return 1)
 * undef SLASH_PROC_OK  NO METHOD! (return 1)
 *
 * alpha	->	cpus detected\t\t: <nr>\n
 * sparc	->	ncpus active\t: <nr>\n
 *
 * default	->	processor\t: <cpunr>\n	(one per cpu)
 */

#if !defined(SLASH_PROC_OK) || defined(__arm__)
int __sc_nr_cpus() {
  return 1;	/* kludge kludge ;-) */
}
#else
#if defined(__alpha__)
#define NR_CPUS_STR "cpus detected"
#define NR_CPUS_OFF 17
#elif defined(__sparc__)
#define NR_CPUS_STR "ncpus active"
#define NR_CPUS_OFF 15
#else
#define NR_CPUS_STR "processor"
#define NR_CPUS_OFF 0 /* count instances of ^NR_CPUS_STR */
#endif
#define NR_CPUS_LEN (strlen(NR_CPUS_STR))
int __sc_nr_cpus(void);
int __sc_nr_cpus() {
#if NR_CPUS_OFF == 0
  int nr=0;
#endif
  char buf[1024]; /* holds ~2 cpuinfos */
  register int nb;
  int wrap=0;
  int fd=open("/proc/cpuinfo", O_RDONLY);

  if (fd==-1) return 1; /* fallback if no proc-fs mounted */

  while((nb=read(fd,buf+wrap,sizeof(buf)-wrap))>0 || wrap) {
    register int idx=0;
    if (nb<0) nb=0;				/* wrapped buffer after err */
    nb+=wrap;
    wrap=0;
    while (idx<nb) {
      if (idx+256>=sizeof(buf)) {		/* avoid split-line parsing */
	memcpy(buf,buf+idx,(wrap=nb-idx));	/* save tail of full buf */
	break;
      }
      if (!memcmp(buf+idx,NR_CPUS_STR,NR_CPUS_LEN)) {
#if NR_CPUS_OFF == 0
	++nr;
#else
	close(fd);
	return atoi(buf+idx+NR_CPUS_OFF);
#endif
      }
      while(idx<nb && buf[idx++]!='\n');	/* skip rest of line */
    }
  }
#if NR_CPUS_OFF == 0
  close(fd);
  return nr;
#endif
}
#endif
========================================================================

>Bela<