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>
This commit is contained in:
Corinna Vinschen 2015-08-29 09:16:47 +02:00
parent 35d5d87540
commit 38fd7ddb79
9 changed files with 561 additions and 65 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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"

View File

@ -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

View File

@ -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.

View File

@ -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 *) &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

View File

@ -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

View File

@ -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>