This is the mail archive of the cygwin-cvs@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[newlib-cygwin] Allow sysconf to return CPU cache information


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=38fd7ddb790609db3e91540d183fb8b62250d969

commit 38fd7ddb790609db3e91540d183fb8b62250d969
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sat Aug 29 09:16:47 2015 +0200

    Allow sysconf to return CPU cache information
    
            * include/sys/unistd.h (_SC_LEVEL*): Add cache-related variables as
            on Linux.
    
            * fhandler_proc.cc (format_proc_cpuinfo): Fetch cache information
            from new cache functions in sysconf.cc, get_cpu_cache_intel and
            get_cpu_cache_amd.
            * sysconf.cc (__nt_query_system): New local helper.
            (get_nproc_values): Utilize __nt_query_system on pre-Windows 7 systems.
            Use GetLogicalProcessorInformationEx otherwise to handle more than
            64 CPUs.  Only handle _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN.
            (get_phys_pages): New helper to handle _SC_PHYS_PAGES.
            (cpuid2_cache_descriptor): New array to map Intel CPUID 2 descriptor
            values to cache type, cache size, associativity and linesize.
            (cpuid2_cache_desc_compar): Comparision function for bsearch over
            cpuid2_cache_descriptor.
            (get_cpu_cache_intel_cpuid2): New function to fetch cache info from
            Intel CPUID 2.
            (get_cpu_cache_intel_cpuid4): Ditto from Intel CPUID 4.
            (get_cpu_cache_intel): New function as CPU-specific entry point.
            (assoc): New array to map associativity values from AMD CPUID
            0x80000006.
            (get_cpu_cache_amd): New function to fetch cache info from AMD CPUIDs
            0x80000005 and 0x80000006.
            (get_cpu_cache): New function to fetch cache info.
            (sca): Call get_phys_pages if _SC_PHYS_PAGES is requested.  Call
            get_cpu_cache for new _SC_* cache requests.
            (SC_MAX): Set to _SC_LEVEL4_CACHE_LINESIZE.
            (get_phys_pages(void)): Call get_phys_pages(int).
            * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump.
    
            * new-features.xml (ov-new2.3): Document sysconf cache addition.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 newlib/ChangeLog                       |   5 +
 newlib/libc/include/sys/unistd.h       |  15 ++
 winsup/cygwin/ChangeLog                |  29 ++
 winsup/cygwin/fhandler_proc.cc         |  65 +++--
 winsup/cygwin/include/cygwin/version.h |   3 +-
 winsup/cygwin/release/2.3.0            |  10 +
 winsup/cygwin/sysconf.cc               | 480 +++++++++++++++++++++++++++++++--
 winsup/doc/ChangeLog                   |   4 +
 winsup/doc/new-features.xml            |  11 +
 9 files changed, 559 insertions(+), 63 deletions(-)

diff --git a/newlib/ChangeLog b/newlib/ChangeLog
index 674ff12..a19a908 100644
--- a/newlib/ChangeLog
+++ b/newlib/ChangeLog
@@ -1,3 +1,8 @@
+2015-08-29  Corinna Vinschen  <corinna@vinschen.de>
+
+	* include/sys/unistd.h (_SC_LEVEL*): Add cache-related variables as
+	on Linux.
+
 2015-08-27  Markus Eisenmann  <meisenmann.lba@fh-salzburg.ac.at>
 
 	* libc/machine/arm/strlen-armv7.S: Fix prepocessor check to avoid
diff --git a/newlib/libc/include/sys/unistd.h b/newlib/libc/include/sys/unistd.h
index eb26921..a1ad12e 100644
--- a/newlib/libc/include/sys/unistd.h
+++ b/newlib/libc/include/sys/unistd.h
@@ -425,6 +425,21 @@ int	_EXFUN(unlinkat, (int, const char *, int));
 #define _SC_THREAD_ROBUST_PRIO_INHERIT  122
 #define _SC_THREAD_ROBUST_PRIO_PROTECT  123
 #define _SC_XOPEN_UUCP                  124
+#define _SC_LEVEL1_ICACHE_SIZE          125
+#define _SC_LEVEL1_ICACHE_ASSOC         126
+#define _SC_LEVEL1_ICACHE_LINESIZE      127
+#define _SC_LEVEL1_DCACHE_SIZE          128
+#define _SC_LEVEL1_DCACHE_ASSOC         129
+#define _SC_LEVEL1_DCACHE_LINESIZE      130
+#define _SC_LEVEL2_CACHE_SIZE           131
+#define _SC_LEVEL2_CACHE_ASSOC          132
+#define _SC_LEVEL2_CACHE_LINESIZE       133
+#define _SC_LEVEL3_CACHE_SIZE           134
+#define _SC_LEVEL3_CACHE_ASSOC          135
+#define _SC_LEVEL3_CACHE_LINESIZE       136
+#define _SC_LEVEL4_CACHE_SIZE           137
+#define _SC_LEVEL4_CACHE_ASSOC          138
+#define _SC_LEVEL4_CACHE_LINESIZE       139
 
 /*
  *  pathconf values per IEEE Std 1003.1, 2008 Edition
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index c76eb7a..d9ab975 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,32 @@
+2015-08-29  Corinna Vinschen  <corinna@vinschen.de>
+
+	* fhandler_proc.cc (format_proc_cpuinfo): Fetch cache information
+	from new cache functions in sysconf.cc, get_cpu_cache_intel and
+	get_cpu_cache_amd.
+	* sysconf.cc (__nt_query_system): New local helper.
+	(get_nproc_values): Utilize __nt_query_system on pre-Windows 7 systems.
+	Use GetLogicalProcessorInformationEx otherwise to handle more than
+	64 CPUs.  Only handle _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN.
+	(get_phys_pages): New helper to handle _SC_PHYS_PAGES.
+	(cpuid2_cache_descriptor): New array to map Intel CPUID 2 descriptor
+	values to cache type, cache size, associativity and linesize.
+	(cpuid2_cache_desc_compar): Comparision function for bsearch over
+	cpuid2_cache_descriptor.
+	(get_cpu_cache_intel_cpuid2): New function to fetch cache info from
+	Intel CPUID 2.
+	(get_cpu_cache_intel_cpuid4): Ditto from Intel CPUID 4.
+	(get_cpu_cache_intel): New function as CPU-specific entry point.
+	(assoc): New array to map associativity values from AMD CPUID
+	0x80000006.
+	(get_cpu_cache_amd): New function to fetch cache info from AMD CPUIDs
+	0x80000005 and 0x80000006.
+	(get_cpu_cache): New function to fetch cache info.
+	(sca): Call get_phys_pages if _SC_PHYS_PAGES is requested.  Call
+	get_cpu_cache for new _SC_* cache requests.
+	(SC_MAX): Set to _SC_LEVEL4_CACHE_LINESIZE.
+	(get_phys_pages(void)): Call get_phys_pages(int).
+	* include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump.
+
 2015-08-27  Corinna Vinschen  <corinna@vinschen.de>
 
 	* autoload.cc (DiscardVirtualMemory): Import.
diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
index fca8265..d34bf31 100644
--- a/winsup/cygwin/fhandler_proc.cc
+++ b/winsup/cygwin/fhandler_proc.cc
@@ -758,47 +758,46 @@ format_proc_cpuinfo (void *, char *&destbuf)
 	cache_alignment = clflush * 2;
       if (is_intel)
 	{
-	  uint32_t cache_level = 0;
-	  uint32_t info, layout, sets;
+	  extern long get_cpu_cache_intel (int sysc, uint32_t maxf);
+	  long cs;
 
-	  for (int idx = 0; ; ++idx)
+	  /* As on Linux, don't check for L3 cache. */
+	  cs = get_cpu_cache_intel (_SC_LEVEL2_CACHE_SIZE, maxf);
+	  if (cs == -1)
 	    {
-	      cpuid (&info, &layout, &sets, &unused, 0x00000004, idx);
-	      uint32_t cache_type = (info & 0x1f);
-	      if (cache_type == 0)
-		break;
-	      uint32_t cur_level = ((info >> 5) & 0x7);
-	      uint32_t ways = ((layout >> 22) & 0x3ff) + 1;
-	      uint32_t part = ((layout >> 12) & 0x3ff) + 1;
-	      uint32_t line = (layout & 0xfff) + 1;
-	      sets++;
-	      if (cur_level == cache_level)
-		cache_size += ways * part * line * sets;
-	      else if (cur_level > cache_level)
-		{
-		  cache_size = ways * part * line * sets;
-		  cache_level = cur_level;
-		}
+	      cs = get_cpu_cache_intel (_SC_LEVEL1_ICACHE_SIZE, maxf);
+	      if (cs != -1)
+		cache_size = cs;
+	      cs = get_cpu_cache_intel (_SC_LEVEL1_DCACHE_SIZE, maxf);
+	      if (cs != -1)
+		cache_size += cs;
 	    }
+	  else
+	    cache_size = cs;
 	  if (cache_size != -1)
 	    cache_size >>= 10;
 	}
-      /* L2 Cache and L2 TLB Identifiers. */
-      if (cache_size == -1 && maxe >= 0x80000006)
-	{
-	  uint32_t l2;
-	  cpuid (&unused, &unused, &l2, &unused, 0x80000006);
-
-	  cache_size = l2 >> 16;
-	}
-      /* L1 Cache and TLB Identifiers. */
-      if (cache_size == -1 && maxe >= 0x80000005)
+      else if (is_amd)
 	{
-	  uint32_t data_cache, inst_cache;
-	  cpuid (&unused, &unused, &data_cache, &inst_cache,
-		 0x80000005);
+	  extern long get_cpu_cache_amd (int sysc, uint32_t maxe);
+	  long cs;
 
-	  cache_size = (inst_cache >> 24) + (data_cache >> 24);
+	  cs = get_cpu_cache_amd (_SC_LEVEL3_CACHE_SIZE, maxe);
+	  if (cs == -1)
+	    cs = get_cpu_cache_amd (_SC_LEVEL2_CACHE_SIZE, maxe);
+	  if (cs == -1)
+	    {
+	      cs = get_cpu_cache_amd (_SC_LEVEL1_ICACHE_SIZE, maxe);
+	      if (cs != -1)
+		cache_size = cs;
+	      cs = get_cpu_cache_amd (_SC_LEVEL1_DCACHE_SIZE, maxe);
+	      if (cs != -1)
+		cache_size += cs;
+	    }
+	  else
+	    cache_size = cs;
+	  if (cache_size != -1)
+	    cache_size >>= 10;
 	}
       bufptr += __small_sprintf (bufptr, "cpu family\t: %d\n"
 					 "model\t\t: %d\n"
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 53842ef..3b14ff4 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -471,13 +471,14 @@ details. */
       287: Export issetugid.
       288: Export getcontext, makecontext, setcontext, swapcontext.
       289: Export sigsetjmp, siglongjmp.
+      290: Add sysconf cache handling.
      */
 
      /* Note that we forgot to bump the api for ualarm, strtoll, strtoull,
 	sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 289
+#define CYGWIN_VERSION_API_MINOR 290
 
      /* There is also a compatibity version number associated with the
 	shared memory regions.  It is incremented when incompatible
diff --git a/winsup/cygwin/release/2.3.0 b/winsup/cygwin/release/2.3.0
index 88b6748..0ce6dc7 100644
--- a/winsup/cygwin/release/2.3.0
+++ b/winsup/cygwin/release/2.3.0
@@ -7,6 +7,13 @@ What's new:
 - posix_madvise(POSIX_MADV_DONTNEED) now utilizes OS functionality available
   starting with Windows 8.1/Server 2012R2.  Still a no-op on older systems.
 
+- sysconf() now supports returning CPU cache information:
+  _SC_LEVEL1_ICACHE_SIZE, _SC_LEVEL1_ICACHE_ASSOC, _SC_LEVEL1_ICACHE_LINESIZE,
+  _SC_LEVEL1_DCACHE_SIZE, _SC_LEVEL1_DCACHE_ASSOC, _SC_LEVEL1_DCACHE_LINESIZE,
+  _SC_LEVEL2_CACHE_SIZE, _SC_LEVEL2_CACHE_ASSOC, _SC_LEVEL2_CACHE_LINESIZE,
+  _SC_LEVEL3_CACHE_SIZE, _SC_LEVEL3_CACHE_ASSOC, _SC_LEVEL3_CACHE_LINESIZE,
+  _SC_LEVEL4_CACHE_SIZE, _SC_LEVEL4_CACHE_ASSOC, _SC_LEVEL4_CACHE_LINESIZE
+
 
 What changed:
 -------------
@@ -22,3 +29,6 @@ Bug Fixes
 - Fix long-standing potential SEGV on 32 bit Cygwin when the dynamic loader
   for OS functions fails to load a function on Windows 7 or later.
   Addresses: No actual bug report known.
+
+- sysconf _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN now handle more than
+  64 CPUs on Windows 7 and later.
diff --git a/winsup/cygwin/sysconf.cc b/winsup/cygwin/sysconf.cc
index 7d09f53..b9bcb2b 100644
--- a/winsup/cygwin/sysconf.cc
+++ b/winsup/cygwin/sysconf.cc
@@ -1,7 +1,7 @@
 /* sysconf.cc
 
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-   2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc.
+   2007, 2008, 2009, 2010, 2011, 2012, 2013, 2015 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -20,6 +20,8 @@ details. */
 #include "dtable.h"
 #include "pinfo.h"
 #include "ntdll.h"
+#include "tls_pbuf.h"
+#include "cpuid.h"
 
 static long
 get_open_max (int in)
@@ -36,38 +38,79 @@ get_page_size (int in)
   return wincap.allocation_granularity ();
 }
 
-static long
-get_nproc_values (int in)
+static bool
+__nt_query_system (PSYSTEM_BASIC_INFORMATION psbi)
 {
   NTSTATUS status;
-  SYSTEM_BASIC_INFORMATION sbi;
 
-  status = NtQuerySystemInformation (SystemBasicInformation, (PVOID) &sbi,
-				     sizeof sbi, NULL);
-  if (!NT_SUCCESS (status))
+  status = NtQuerySystemInformation (SystemBasicInformation, (PVOID) psbi,
+				     sizeof *psbi, NULL);
+  return NT_SUCCESS (status);
+}
+
+#define add_size(p,s) ((p) = ((__typeof__(p))((PBYTE)(p)+(s))))
+
+static long
+get_nproc_values (int in)
+{
+  if (!wincap.has_processor_groups ())	/* Pre Windows 7 */
     {
-      __seterrno_from_nt_status (status);
-      debug_printf ("NtQuerySystemInformation: status %y, %E", status);
-      return -1;
+      SYSTEM_BASIC_INFORMATION sbi;
+
+      if (!__nt_query_system (&sbi))
+	return -1;
+      switch (in)
+	{
+	case _SC_NPROCESSORS_CONF:
+	  return sbi.NumberProcessors;
+	case _SC_NPROCESSORS_ONLN:
+	  {
+	    int i = 0;
+	    do
+	     if (sbi.ActiveProcessors & 1)
+	       i++;
+	    while (sbi.ActiveProcessors >>= 1);
+	    return i;
+	  }
+	}
     }
-  switch (in)
-    {
-    case _SC_NPROCESSORS_CONF:
-      return sbi.NumberProcessors;
-    case _SC_NPROCESSORS_ONLN:
+
+  tmp_pathbuf tp;
+  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX lpi, plpi;
+  DWORD lpi_size = NT_MAX_PATH;
+  long cnt = 0;
+
+  lpi = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) tp.c_get ();
+  if (!GetLogicalProcessorInformationEx (RelationGroup, lpi, &lpi_size))
+    return -1;
+  plpi = lpi;
+  for (DWORD size = lpi_size; size > 0;
+       size -= plpi->Size, add_size (plpi, plpi->Size))
+    if (plpi->Relationship == RelationGroup)
       {
-	int i = 0;
-	do
-	 if (sbi.ActiveProcessors & 1)
-	   i++;
-	while (sbi.ActiveProcessors >>= 1);
-	return i;
+	for (WORD i = 0; i < plpi->Group.MaximumGroupCount; ++i)
+	  switch (in)
+	    {
+	    case _SC_NPROCESSORS_CONF:
+	      cnt += plpi->Group.GroupInfo[0].MaximumProcessorCount;
+	      break;
+	    case _SC_NPROCESSORS_ONLN:
+	      cnt += plpi->Group.GroupInfo[0].ActiveProcessorCount;
+	      break;
+	    }
       }
-    case _SC_PHYS_PAGES:
-      return sbi.NumberOfPhysicalPages
-	     / (wincap.allocation_granularity () / wincap.page_size ());
-    }
-  return -1;
+  return cnt;
+}
+
+static long
+get_phys_pages (int in)
+{
+  SYSTEM_BASIC_INFORMATION sbi;
+
+  if (!__nt_query_system (&sbi))
+    return -1;
+  return sbi.NumberOfPhysicalPages
+	 / (wincap.allocation_granularity () / wincap.page_size ());
 }
 
 static long
@@ -88,6 +131,370 @@ get_avphys (int in)
 	 / (wincap.allocation_granularity () / wincap.page_size ());
 }
 
+enum cache_level
+{
+  LevelNone,
+  Level1I,
+  Level1D,
+  Level2,
+  Level3,
+  Level4
+};
+
+struct cpuid2_cache_desc
+{
+  uint8_t	desc;
+  cache_level	level;
+  uint32_t	size;
+  uint32_t	assoc;
+  uint32_t	linesize;
+};
+
+static const cpuid2_cache_desc cpuid2_cache_descriptor[] =
+{
+  { 0x06, Level1I,     8,  4, 32 },
+  { 0x08, Level1I,    16,  4, 32 },
+  { 0x09, Level1I,    32,  4, 64 },
+  { 0x0a, Level1D,     8,  2, 32 },
+  { 0x0c, Level1D,    16,  4, 32 },
+  { 0x0d, Level1D,    16,  4, 64 },
+  { 0x0e, Level1D,    24,  6, 64 },
+  { 0x21, Level2,    256,  8, 64 },
+  { 0x22, Level3,    512,  4, 64 },
+  { 0x23, Level3,   1024,  8, 64 },
+  { 0x25, Level3,   2048,  8, 64 },
+  { 0x29, Level3,   4096,  8, 64 },
+  { 0x2c, Level1D,    32,  8, 64 },
+  { 0x30, Level1I,    32,  8, 64 },
+  { 0x39, Level2,    128,  4, 64 },
+  { 0x3a, Level2,    192,  6, 64 },
+  { 0x3b, Level2,    128,  2, 64 },
+  { 0x3c, Level2,    256,  4, 64 },
+  { 0x3d, Level2,    384,  6, 64 },
+  { 0x3e, Level2,    512,  4, 64 },
+  { 0x3f, Level2,    256,  2, 64 },
+  { 0x41, Level2,    128,  4, 32 },
+  { 0x42, Level2,    256,  4, 32 },
+  { 0x43, Level2,    512,  4, 32 },
+  { 0x44, Level2,   1024,  4, 32 },
+  { 0x45, Level2,   2048,  4, 32 },
+  { 0x46, Level3,   4096,  4, 64 },
+  { 0x47, Level3,   8192,  8, 64 },
+  { 0x48, Level2,   3072, 12, 64 },
+  { 0x49, Level3,   4096, 16, 64 },
+  { 0x4a, Level3,   6144, 12, 64 },
+  { 0x4b, Level3,   8192, 16, 64 },
+  { 0x4c, Level3,  12288, 12, 64 },
+  { 0x4d, Level3,  16384, 16, 64 },
+  { 0x4e, Level2,   6144, 24, 64 },
+  { 0x60, Level1D,    16,  8, 64 },
+  { 0x66, Level1D,     8,  4, 64 },
+  { 0x67, Level1D,    16,  4, 64 },
+  { 0x68, Level1D,    32,  4, 64 },
+  { 0x78, Level2,   1024,  4, 64 },
+  { 0x79, Level2,    128,  8, 64 },
+  { 0x7a, Level2,    256,  8, 64 },
+  { 0x7b, Level2,    512,  8, 64 },
+  { 0x7c, Level2,   1024,  8, 64 },
+  { 0x7d, Level2,   2048,  8, 64 },
+  { 0x7f, Level2,    512,  2, 64 },
+  { 0x80, Level2,    512,  8, 64 },
+  { 0x82, Level2,    256,  8, 32 },
+  { 0x83, Level2,    512,  8, 32 },
+  { 0x84, Level2,   1024,  8, 32 },
+  { 0x85, Level2,   2048,  8, 32 },
+  { 0x86, Level2,    512,  4, 64 },
+  { 0x87, Level2,   1024,  8, 64 },
+  { 0xd0, Level3,    512,  4, 64 },
+  { 0xd1, Level3,   1024,  4, 64 },
+  { 0xd2, Level3,   2048,  4, 64 },
+  { 0xd6, Level3,   1024,  8, 64 },
+  { 0xd7, Level3,   2048,  8, 64 },
+  { 0xd8, Level3,   4096, 12, 64 },
+  { 0xdc, Level3,   2048, 12, 64 },
+  { 0xdd, Level3,   4096, 12, 64 },
+  { 0xde, Level3,   8192, 12, 64 },
+  { 0xe2, Level3,   2048, 16, 64 },
+  { 0xe3, Level3,   4096, 16, 64 },
+  { 0xe4, Level3,   8192, 16, 64 },
+  { 0xea, Level3,  12288, 24, 64 },
+  { 0xeb, Level3,  18432, 24, 64 },
+  { 0xec, Level3,  24576, 24, 64 },
+};
+
+static int
+cpuid2_cache_desc_compar (const void *key, const void *memb)
+{
+  cpuid2_cache_desc *ckey = (cpuid2_cache_desc *) key;
+  cpuid2_cache_desc *cmemb = (cpuid2_cache_desc *) memb;
+  return ckey->desc - cmemb->desc;
+}
+
+static long
+get_cpu_cache_intel_cpuid2 (int in)
+{
+  uint32_t reg[4];
+  long ret = 0;
+  int num;
+
+  cpuid (reg, reg + 1, reg + 2, reg + 3, 0x00000002);
+  num = reg[0] & 0xff;
+  for (int i = 0; i < num; ++i)
+    {
+      cpuid (reg, reg + 1, reg + 2, reg + 3, 0x00000002);
+      for (int r = 0; r < 4; ++r)
+	{
+	  if (reg[r] & 0x80000000)
+	    continue;
+	  for (int b = (r == 0) ? 1 : 0; b < 4; ++b)
+	    {
+	      cpuid2_cache_desc key, *cdp;
+
+	      key.desc = ((uint8_t *) &reg[r])[b];
+	      cdp = (cpuid2_cache_desc *)
+			bsearch (&key, cpuid2_cache_descriptor,
+				 sizeof cpuid2_cache_descriptor
+				 / sizeof *cpuid2_cache_descriptor,
+				 sizeof *cpuid2_cache_descriptor,
+				 cpuid2_cache_desc_compar);
+	      if (!cdp)
+		continue;
+	      switch (in)
+		{
+		case _SC_LEVEL1_ICACHE_SIZE:
+		  if (cdp->level == Level1I)
+		    ret += cdp->size * 1024;
+		  break;
+		case _SC_LEVEL1_ICACHE_ASSOC:
+		  if (cdp->level == Level1I)
+		    return cdp->assoc;
+		  break;
+		case _SC_LEVEL1_ICACHE_LINESIZE:
+		  if (cdp->level == Level1I)
+		    return cdp->linesize;
+		  break;
+		case _SC_LEVEL1_DCACHE_SIZE:
+		  if (cdp->level == Level1D)
+		    ret += cdp->size * 1024;
+		  break;
+		case _SC_LEVEL1_DCACHE_ASSOC:
+		  if (cdp->level == Level1D)
+		    return cdp->assoc;
+		  break;
+		case _SC_LEVEL1_DCACHE_LINESIZE:
+		  if (cdp->level == Level1D)
+		    return cdp->linesize;
+		  break;
+		case _SC_LEVEL2_CACHE_SIZE:
+		  if (cdp->level == Level2)
+		    ret += cdp->size * 1024;
+		  break;
+		case _SC_LEVEL2_CACHE_ASSOC:
+		  if (cdp->level == Level2)
+		    return cdp->assoc;
+		  break;
+		case _SC_LEVEL2_CACHE_LINESIZE:
+		  if (cdp->level == Level2)
+		    return cdp->linesize;
+		  break;
+		case _SC_LEVEL3_CACHE_SIZE:
+		  if (cdp->level == Level3)
+		    ret += cdp->size * 1024;
+		  break;
+		case _SC_LEVEL3_CACHE_ASSOC:
+		  if (cdp->level == Level3)
+		    return cdp->assoc;
+		  break;
+		case _SC_LEVEL3_CACHE_LINESIZE:
+		  if (cdp->level == Level3)
+		    return cdp->linesize;
+		  break;
+		}
+	    }
+	}
+    }
+  return ret;
+}
+
+static long
+get_cpu_cache_intel_cpuid4 (int in)
+{
+  uint32_t eax, ebx, ecx, edx;
+  long ret = 0;
+
+  for (int idx = 0; ; ++idx)
+    {
+      uint32_t cache_type, cur_level, assoc, part, linesize, sets;
+
+      cpuid (&eax, &ebx, &ecx, &edx, 0x00000004, idx);
+      if ((cache_type = (eax & 0x1f))== 0)
+	break;
+      cur_level = ((eax >> 5) & 0x7);
+      assoc = ((ebx >> 22) & 0x3ff) + 1;
+      part = ((ebx >> 12) & 0x3ff) + 1;
+      linesize = (ebx & 0xfff) + 1;
+      sets = ecx + 1;
+      switch (in)
+	{
+	case _SC_LEVEL1_ICACHE_SIZE:
+	  if (cur_level == 1 && cache_type == 2)
+	    ret += assoc * part * linesize * sets;
+	  break;
+	case _SC_LEVEL1_ICACHE_ASSOC:
+	  if (cur_level == 1 && cache_type == 2)
+	    return assoc;
+	case _SC_LEVEL1_ICACHE_LINESIZE:
+	  if (cur_level == 1 && cache_type == 2)
+	    return linesize;
+	case _SC_LEVEL1_DCACHE_SIZE:
+	  if (cur_level == 1 && cache_type == 1)
+	    ret += assoc * part * linesize * sets;
+	  break;
+	case _SC_LEVEL1_DCACHE_ASSOC:
+	  if (cur_level == 1 && cache_type == 1)
+	    return assoc;
+	case _SC_LEVEL1_DCACHE_LINESIZE:
+	  if (cur_level == 1 && cache_type == 1)
+	    return linesize;
+	case _SC_LEVEL2_CACHE_SIZE:
+	  if (cur_level == 2)
+	    ret += assoc * part * linesize * sets;
+	  break;
+	case _SC_LEVEL2_CACHE_ASSOC:
+	  if (cur_level == 2)
+	    return assoc;
+	case _SC_LEVEL2_CACHE_LINESIZE:
+	  if (cur_level == 2)
+	    return linesize;
+	case _SC_LEVEL3_CACHE_SIZE:
+	  if (cur_level == 3)
+	    ret += assoc * part * linesize * sets;
+	  break;
+	case _SC_LEVEL3_CACHE_ASSOC:
+	  if (cur_level == 3)
+	    return assoc;
+	case _SC_LEVEL3_CACHE_LINESIZE:
+	  if (cur_level == 3)
+	    return linesize;
+	}
+    }
+  return ret;
+}
+
+/* Also called from format_proc_cpuinfo */
+long
+get_cpu_cache_intel (int in, uint32_t maxf)
+{
+  long ret = 0;
+
+  switch (in)
+    {
+    case _SC_LEVEL1_ICACHE_SIZE:
+    case _SC_LEVEL1_ICACHE_ASSOC:
+    case _SC_LEVEL1_ICACHE_LINESIZE:
+    case _SC_LEVEL1_DCACHE_SIZE:
+    case _SC_LEVEL1_DCACHE_ASSOC:
+    case _SC_LEVEL1_DCACHE_LINESIZE:
+    case _SC_LEVEL2_CACHE_SIZE:
+    case _SC_LEVEL2_CACHE_ASSOC:
+    case _SC_LEVEL2_CACHE_LINESIZE:
+    case _SC_LEVEL3_CACHE_SIZE:
+    case _SC_LEVEL3_CACHE_ASSOC:
+    case _SC_LEVEL3_CACHE_LINESIZE:
+      if (maxf >= 4)
+	ret = get_cpu_cache_intel_cpuid4 (in);
+      else if (maxf >= 2)
+	ret = get_cpu_cache_intel_cpuid2 (in);
+      break;
+    default:
+      break;
+    }
+  return ret;
+}
+
+static const long assoc[16] = {  0,  1,  2,  2,  4,  4,   8,      8,
+				16, 16, 32, 48, 64, 96, 128, 0x8000 };
+
+/* Also called from format_proc_cpuinfo */
+long
+get_cpu_cache_amd (int in, uint32_t maxe)
+{
+  uint32_t eax, ebx, ecx, edx;
+  long ret = 0;
+
+  if (in >= _SC_LEVEL1_ICACHE_SIZE && in <= _SC_LEVEL1_DCACHE_LINESIZE
+      && maxe >= 0x80000005)
+    cpuid (&eax, &ebx, &ecx, &edx, 0x80000005);
+  else if (in >= _SC_LEVEL2_CACHE_SIZE && in <= _SC_LEVEL3_CACHE_LINESIZE
+	   && maxe >= 0x80000006)
+    cpuid (&eax, &ebx, &ecx, &edx, 0x80000006);
+
+  switch (in)
+    {
+    case _SC_LEVEL1_ICACHE_SIZE:
+      ret = (edx & 0xff000000) >> 14;
+      break;
+    case _SC_LEVEL1_ICACHE_ASSOC:
+      ret = (edx & 0xff0000) >> 16;
+      if (ret == 0xff)
+	ret = 0x8000;
+      break;
+    case _SC_LEVEL1_ICACHE_LINESIZE:
+      ret = (edx & 0xff);
+      break;
+    case _SC_LEVEL1_DCACHE_SIZE:
+      ret = (ecx & 0xff000000) >> 14;
+      break;
+    case _SC_LEVEL1_DCACHE_ASSOC:
+      ret = (ecx & 0xff0000) >> 16;
+      if (ret == 0xff)
+	ret = 0x8000;
+      break;
+    case _SC_LEVEL1_DCACHE_LINESIZE:
+      ret = (ecx & 0xff);
+      break;
+    case _SC_LEVEL2_CACHE_SIZE:
+      ret = (ecx & 0xffff0000) >> 6;
+      break;
+    case _SC_LEVEL2_CACHE_ASSOC:
+      ret = assoc[(ecx & 0xf000) >> 12];
+      break;
+    case _SC_LEVEL2_CACHE_LINESIZE:
+      ret = (ecx & 0xff);
+      break;
+    case _SC_LEVEL3_CACHE_SIZE:
+      ret = (long) ((edx & 0xfffc0000) >> 18) * 512 * 1024;
+      break;
+    case _SC_LEVEL3_CACHE_ASSOC:
+      ret = assoc[(edx & 0xf000) >> 12];
+      break;
+    case _SC_LEVEL3_CACHE_LINESIZE:
+      ret = (edx & 0xff);
+      break;
+    default:
+      break;
+    }
+  return ret;
+}
+
+static long
+get_cpu_cache (int in)
+{
+  uint32_t maxf, vendor_id[4];
+  cpuid (&maxf, &vendor_id[0], &vendor_id[2], &vendor_id[1], 0x00000000);
+
+  vendor_id[3] = 0;
+  if (!strcmp ((char*) vendor_id, "GenuineIntel"))
+    return get_cpu_cache_intel (in, maxf & 0xffff);
+  else if (!strcmp ((char*)vendor_id, "AuthenticAMD"))
+    {
+      uint32_t maxe = 0, unused;
+      cpuid (&maxe, &unused, &unused, &unused, 0x80000000);
+      return get_cpu_cache_amd (in, maxe);
+    }
+  return 0;
+}
+
 enum sc_type { nsup, cons, func };
 
 static struct
@@ -111,7 +518,7 @@ static struct
   {func, {f:get_page_size}},		/*   8, _SC_PAGESIZE */
   {func, {f:get_nproc_values}},		/*   9, _SC_NPROCESSORS_CONF */
   {func, {f:get_nproc_values}},		/*  10, _SC_NPROCESSORS_ONLN */
-  {func, {f:get_nproc_values}},		/*  11, _SC_PHYS_PAGES */
+  {func, {f:get_phys_pages}},		/*  11, _SC_PHYS_PAGES */
   {func, {f:get_avphys}},		/*  12, _SC_AVPHYS_PAGES */
   {cons, {c:MQ_OPEN_MAX}},		/*  13, _SC_MQ_OPEN_MAX */
   {cons, {c:MQ_PRIO_MAX}},		/*  14, _SC_MQ_PRIO_MAX */
@@ -225,10 +632,25 @@ static struct
   {cons, {c:-1L}},			/* 122, _SC_THREAD_ROBUST_PRIO_INHERIT */
   {cons, {c:-1L}},			/* 123, _SC_THREAD_ROBUST_PRIO_PROTECT */
   {cons, {c:-1L}},			/* 124, _SC_XOPEN_UUCP */
+  {func, {f:get_cpu_cache}},		/* 125, _SC_LEVEL1_ICACHE_SIZE */
+  {func, {f:get_cpu_cache}},		/* 126, _SC_LEVEL1_ICACHE_ASSOC */
+  {func, {f:get_cpu_cache}},		/* 127, _SC_LEVEL1_ICACHE_LINESIZE */
+  {func, {f:get_cpu_cache}},		/* 128, _SC_LEVEL1_DCACHE_SIZE */
+  {func, {f:get_cpu_cache}},		/* 129, _SC_LEVEL1_DCACHE_ASSOC */
+  {func, {f:get_cpu_cache}},		/* 130, _SC_LEVEL1_DCACHE_LINESIZE */
+  {func, {f:get_cpu_cache}},		/* 131, _SC_LEVEL2_CACHE_SIZE */
+  {func, {f:get_cpu_cache}},		/* 132, _SC_LEVEL2_CACHE_ASSOC */
+  {func, {f:get_cpu_cache}},		/* 133, _SC_LEVEL2_CACHE_LINESIZE */
+  {func, {f:get_cpu_cache}},		/* 134, _SC_LEVEL3_CACHE_SIZE */
+  {func, {f:get_cpu_cache}},		/* 135, _SC_LEVEL3_CACHE_ASSOC */
+  {func, {f:get_cpu_cache}},		/* 136, _SC_LEVEL3_CACHE_LINESIZE */
+  {func, {f:get_cpu_cache}},		/* 137, _SC_LEVEL4_CACHE_SIZE */
+  {func, {f:get_cpu_cache}},		/* 138, _SC_LEVEL4_CACHE_ASSOC */
+  {func, {f:get_cpu_cache}},		/* 139, _SC_LEVEL4_CACHE_LINESIZE */
 };
 
 #define SC_MIN _SC_ARG_MAX
-#define SC_MAX _SC_XOPEN_UUCP
+#define SC_MAX _SC_LEVEL4_CACHE_LINESIZE
 
 /* sysconf: POSIX 4.8.1.1 */
 /* Allows a portable app to determine quantities of resources or
@@ -335,7 +757,7 @@ get_nprocs (void)
 extern "C" long
 get_phys_pages (void)
 {
-  return get_nproc_values (_SC_PHYS_PAGES);
+  return get_phys_pages (_SC_PHYS_PAGES);
 }
 
 extern "C" long
diff --git a/winsup/doc/ChangeLog b/winsup/doc/ChangeLog
index c645c67..7587d68 100644
--- a/winsup/doc/ChangeLog
+++ b/winsup/doc/ChangeLog
@@ -1,3 +1,7 @@
+2015-08-29  Corinna Vinschen  <corinna@vinschen.de>
+
+	* new-features.xml (ov-new2.3): Document sysconf cache addition.
+
 2015-08-27  Corinna Vinschen  <corinna@vinschen.de>
 
 	* new-features.xml (ov-new2.3): New section, document posix_madvise
diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml
index edd1963..fc53b01 100644
--- a/winsup/doc/new-features.xml
+++ b/winsup/doc/new-features.xml
@@ -16,6 +16,17 @@ posix_madvise(POSIX_MADV_DONTNEED) now utilizes OS functionality available
 starting with Windows 8.1/Server 2012R2.
 </para></listitem>
 
+<listitem><para>
+sysconf() now supports returning CPU cache information:
+  <screen>
+  _SC_LEVEL1_ICACHE_SIZE, _SC_LEVEL1_ICACHE_ASSOC, _SC_LEVEL1_ICACHE_LINESIZE,
+  _SC_LEVEL1_DCACHE_SIZE, _SC_LEVEL1_DCACHE_ASSOC, _SC_LEVEL1_DCACHE_LINESIZE,
+  _SC_LEVEL2_CACHE_SIZE, _SC_LEVEL2_CACHE_ASSOC, _SC_LEVEL2_CACHE_LINESIZE,
+  _SC_LEVEL3_CACHE_SIZE, _SC_LEVEL3_CACHE_ASSOC, _SC_LEVEL3_CACHE_LINESIZE,
+  _SC_LEVEL4_CACHE_SIZE, _SC_LEVEL4_CACHE_ASSOC, _SC_LEVEL4_CACHE_LINESIZE
+  </screen>
+</para></listitem>
+
 </itemizedlist>
 
 </sect2>


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]