qemu/util/cacheinfo.c
<<
>>
Prefs
   1/*
   2 * cacheinfo.c - helpers to query the host about its caches
   3 *
   4 * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
   5 * License: GNU GPL, version 2 or later.
   6 *   See the COPYING file in the top-level directory.
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "qemu/host-utils.h"
  11#include "qemu/atomic.h"
  12
  13int qemu_icache_linesize = 0;
  14int qemu_icache_linesize_log;
  15int qemu_dcache_linesize = 0;
  16int qemu_dcache_linesize_log;
  17
  18/*
  19 * Operating system specific detection mechanisms.
  20 */
  21
  22#if defined(_WIN32)
  23
  24static void sys_cache_info(int *isize, int *dsize)
  25{
  26    SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf;
  27    DWORD size = 0;
  28    BOOL success;
  29    size_t i, n;
  30
  31    /* Check for the required buffer size first.  Note that if the zero
  32       size we use for the probe results in success, then there is no
  33       data available; fail in that case.  */
  34    success = GetLogicalProcessorInformation(0, &size);
  35    if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  36        return;
  37    }
  38
  39    n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
  40    size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
  41    buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n);
  42    if (!GetLogicalProcessorInformation(buf, &size)) {
  43        goto fail;
  44    }
  45
  46    for (i = 0; i < n; i++) {
  47        if (buf[i].Relationship == RelationCache
  48            && buf[i].Cache.Level == 1) {
  49            switch (buf[i].Cache.Type) {
  50            case CacheUnified:
  51                *isize = *dsize = buf[i].Cache.LineSize;
  52                break;
  53            case CacheInstruction:
  54                *isize = buf[i].Cache.LineSize;
  55                break;
  56            case CacheData:
  57                *dsize = buf[i].Cache.LineSize;
  58                break;
  59            default:
  60                break;
  61            }
  62        }
  63    }
  64 fail:
  65    g_free(buf);
  66}
  67
  68#elif defined(__APPLE__) \
  69      || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  70# include <sys/sysctl.h>
  71# if defined(__APPLE__)
  72#  define SYSCTL_CACHELINE_NAME "hw.cachelinesize"
  73# else
  74#  define SYSCTL_CACHELINE_NAME "machdep.cacheline_size"
  75# endif
  76
  77static void sys_cache_info(int *isize, int *dsize)
  78{
  79    /* There's only a single sysctl for both I/D cache line sizes.  */
  80    long size;
  81    size_t len = sizeof(size);
  82    if (!sysctlbyname(SYSCTL_CACHELINE_NAME, &size, &len, NULL, 0)) {
  83        *isize = *dsize = size;
  84    }
  85}
  86
  87#else
  88/* POSIX */
  89
  90static void sys_cache_info(int *isize, int *dsize)
  91{
  92# ifdef _SC_LEVEL1_ICACHE_LINESIZE
  93    *isize = sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
  94# endif
  95# ifdef _SC_LEVEL1_DCACHE_LINESIZE
  96    *dsize = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
  97# endif
  98}
  99#endif /* sys_cache_info */
 100
 101/*
 102 * Architecture (+ OS) specific detection mechanisms.
 103 */
 104
 105#if defined(__aarch64__)
 106
 107static void arch_cache_info(int *isize, int *dsize)
 108{
 109    if (*isize == 0 || *dsize == 0) {
 110        uint64_t ctr;
 111
 112        /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1,
 113           but (at least under Linux) these are marked protected by the
 114           kernel.  However, CTR_EL0 contains the minimum linesize in the
 115           entire hierarchy, and is used by userspace cache flushing.  */
 116        asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr));
 117        if (*isize == 0) {
 118            *isize = 4 << (ctr & 0xf);
 119        }
 120        if (*dsize == 0) {
 121            *dsize = 4 << ((ctr >> 16) & 0xf);
 122        }
 123    }
 124}
 125
 126#elif defined(_ARCH_PPC) && defined(__linux__)
 127# include "elf.h"
 128
 129static void arch_cache_info(int *isize, int *dsize)
 130{
 131    if (*isize == 0) {
 132        *isize = qemu_getauxval(AT_ICACHEBSIZE);
 133    }
 134    if (*dsize == 0) {
 135        *dsize = qemu_getauxval(AT_DCACHEBSIZE);
 136    }
 137}
 138
 139#else
 140static void arch_cache_info(int *isize, int *dsize) { }
 141#endif /* arch_cache_info */
 142
 143/*
 144 * ... and if all else fails ...
 145 */
 146
 147static void fallback_cache_info(int *isize, int *dsize)
 148{
 149    /* If we can only find one of the two, assume they're the same.  */
 150    if (*isize) {
 151        if (*dsize) {
 152            /* Success! */
 153        } else {
 154            *dsize = *isize;
 155        }
 156    } else if (*dsize) {
 157        *isize = *dsize;
 158    } else {
 159#if defined(_ARCH_PPC)
 160        /* For PPC, we're going to use the icache size computed for
 161           flush_icache_range.  Which means that we must use the
 162           architecture minimum.  */
 163        *isize = *dsize = 16;
 164#else
 165        /* Otherwise, 64 bytes is not uncommon.  */
 166        *isize = *dsize = 64;
 167#endif
 168    }
 169}
 170
 171static void __attribute__((constructor)) init_cache_info(void)
 172{
 173    int isize = 0, dsize = 0;
 174
 175    sys_cache_info(&isize, &dsize);
 176    arch_cache_info(&isize, &dsize);
 177    fallback_cache_info(&isize, &dsize);
 178
 179    assert((isize & (isize - 1)) == 0);
 180    assert((dsize & (dsize - 1)) == 0);
 181
 182    qemu_icache_linesize = isize;
 183    qemu_icache_linesize_log = ctz32(isize);
 184    qemu_dcache_linesize = dsize;
 185    qemu_dcache_linesize_log = ctz32(dsize);
 186
 187    atomic64_init();
 188}
 189