From 38fd7ddb790609db3e91540d183fb8b62250d969 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sat, 29 Aug 2015 09:16:47 +0200 Subject: [PATCH] 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 --- 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 | 484 +++++++++++++++++++++++-- winsup/doc/ChangeLog | 4 + winsup/doc/new-features.xml | 11 + 9 files changed, 561 insertions(+), 65 deletions(-) diff --git a/newlib/ChangeLog b/newlib/ChangeLog index 674ff12be..a19a908c4 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,8 @@ +2015-08-29 Corinna Vinschen + + * include/sys/unistd.h (_SC_LEVEL*): Add cache-related variables as + on Linux. + 2015-08-27 Markus Eisenmann * 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 eb26921cc..a1ad12e05 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 c76eb7a5f..d9ab9753a 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,32 @@ +2015-08-29 Corinna Vinschen + + * 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 * autoload.cc (DiscardVirtualMemory): Import. diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc index fca8265fe..d34bf3135 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) + else if (is_amd) { - uint32_t l2; - cpuid (&unused, &unused, &l2, &unused, 0x80000006); + extern long get_cpu_cache_amd (int sysc, uint32_t maxe); + long cs; - cache_size = l2 >> 16; - } - /* L1 Cache and TLB Identifiers. */ - if (cache_size == -1 && maxe >= 0x80000005) - { - uint32_t data_cache, inst_cache; - cpuid (&unused, &unused, &data_cache, &inst_cache, - 0x80000005); - - 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 53842efb8..3b14ff40d 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 88b67487f..0ce6dc7ba 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 7d09f53d0..b9bcb2b6c 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 bool +__nt_query_system (PSYSTEM_BASIC_INFORMATION psbi) +{ + NTSTATUS 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) { - NTSTATUS status; + if (!wincap.has_processor_groups ()) /* Pre Windows 7 */ + { + 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; + } + } + } + + 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) + { + 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; + } + } + return cnt; +} + +static long +get_phys_pages (int in) +{ SYSTEM_BASIC_INFORMATION sbi; - status = NtQuerySystemInformation (SystemBasicInformation, (PVOID) &sbi, - sizeof sbi, NULL); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - debug_printf ("NtQuerySystemInformation: status %y, %E", status); - 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; - } - case _SC_PHYS_PAGES: - return sbi.NumberOfPhysicalPages - / (wincap.allocation_granularity () / wincap.page_size ()); - } - return -1; + 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 *) ®[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 c645c670d..7587d683e 100644 --- a/winsup/doc/ChangeLog +++ b/winsup/doc/ChangeLog @@ -1,3 +1,7 @@ +2015-08-29 Corinna Vinschen + + * new-features.xml (ov-new2.3): Document sysconf cache addition. + 2015-08-27 Corinna Vinschen * 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 edd19633f..fc53b0138 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. + +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 + + +