cache line locking on AMD x86_64 utilising L3 CAT pseudo-locking
26

Configure Feed

Select the types of activity you want to include in your feed.

icepick / src / topology.c
7.4 kB 311 lines
1#define _GNU_SOURCE 2#include "internal.h" 3#include <stdlib.h> 4#include <string.h> 5#include <cpuid.h> 6#include <sched.h> 7#include <dirent.h> 8#include <stdio.h> 9#include <unistd.h> 10 11static void cpuid(uint32_t leaf, uint32_t subleaf, 12 uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) 13{ 14 __cpuid_count(leaf, subleaf, *eax, *ebx, *ecx, *edx); 15} 16 17static bool detect_cat_support(icepick_topology_t *topo) 18{ 19 uint32_t eax, ebx, ecx, edx; 20 21 cpuid(0x10, 0, &eax, &ebx, &ecx, &edx); 22 if (!(ebx & (1 << 1))) 23 return false; 24 25 cpuid(0x10, 1, &eax, &ebx, &ecx, &edx); 26 27 topo->l3_ways = (eax & 0x1F) + 1; 28 topo->max_clos = (edx & 0xFFFF) + 1; 29 topo->default_way_mask = ebx; 30 31 if (topo->max_clos > MAX_CLOS) 32 topo->max_clos = MAX_CLOS; 33 34 return true; 35} 36 37static bool detect_mba_support(icepick_topology_t *topo) 38{ 39 uint32_t eax, ebx, ecx, edx; 40 41 cpuid(0x10, 0, &eax, &ebx, &ecx, &edx); 42 if (!(ebx & (1 << 3))) 43 return false; 44 45 cpuid(0x10, 3, &eax, &ebx, &ecx, &edx); 46 47 topo->max_mba_thrtl = (eax & 0xFFF) + 1; 48 topo->mba_is_linear = (ecx & (1 << 2)) != 0; 49 50 return true; 51} 52 53static void detect_amd_cache_topology(icepick_topology_t *topo) 54{ 55 uint32_t eax, ebx, ecx, edx; 56 57 for (int i = 0; i < 16; i++) { 58 cpuid(0x8000001D, i, &eax, &ebx, &ecx, &edx); 59 60 uint32_t cache_type = eax & 0x1F; 61 if (cache_type == 0) 62 break; 63 64 uint32_t cache_level = (eax >> 5) & 0x7; 65 if (cache_level != 3) 66 continue; 67 68 uint32_t line_size = (ebx & 0xFFF) + 1; 69 uint32_t partitions = ((ebx >> 12) & 0x3FF) + 1; 70 uint32_t ways = ((ebx >> 22) & 0x3FF) + 1; 71 uint32_t sets = ecx + 1; 72 73 topo->l3_size = (size_t)line_size * partitions * ways * sets; 74 topo->l3_ways = ways; 75 topo->way_size = topo->l3_size / topo->l3_ways; 76 break; 77 } 78} 79 80static void detect_intel_cache_topology(icepick_topology_t *topo) 81{ 82 uint32_t eax, ebx, ecx, edx; 83 84 for (int i = 0; i < 16; i++) { 85 cpuid(0x04, i, &eax, &ebx, &ecx, &edx); 86 87 uint32_t cache_type = eax & 0x1F; 88 if (cache_type == 0) 89 break; 90 91 uint32_t cache_level = (eax >> 5) & 0x7; 92 if (cache_level != 3) 93 continue; 94 95 uint32_t line_size = (ebx & 0xFFF) + 1; 96 uint32_t partitions = ((ebx >> 12) & 0x3FF) + 1; 97 uint32_t ways = ((ebx >> 22) & 0x3FF) + 1; 98 uint32_t sets = ecx + 1; 99 100 topo->l3_size = (size_t)line_size * partitions * ways * sets; 101 topo->l3_ways = ways; 102 topo->way_size = topo->l3_size / topo->l3_ways; 103 break; 104 } 105} 106 107static bool is_amd_cpu(void) 108{ 109 uint32_t eax, ebx, ecx, edx; 110 cpuid(0, 0, &eax, &ebx, &ecx, &edx); 111 return (ebx == 0x68747541 && edx == 0x69746E65 && ecx == 0x444D4163); 112} 113 114static int count_ccxs(void) 115{ 116 DIR *dir = opendir("/sys/devices/system/node"); 117 if (!dir) 118 return 1; 119 120 int count = 0; 121 struct dirent *entry; 122 while ((entry = readdir(dir))) { 123 if (strncmp(entry->d_name, "node", 4) == 0) 124 count++; 125 } 126 closedir(dir); 127 128 return count > 0 ? count : 1; 129} 130 131static void detect_numa_topology(icepick_topology_t *topo) 132{ 133 topo->ccx_count = count_ccxs(); 134 if (topo->ccx_count > MAX_CCXS) 135 topo->ccx_count = MAX_CCXS; 136 137 for (unsigned i = 0; i < topo->ccx_count; i++) 138 topo->ccx_numa_node[i] = i; 139} 140 141static bool detect_hybrid_architecture(icepick_topology_t *topo) 142{ 143 (void)topo; 144 uint32_t eax, ebx, ecx, edx; 145 146 cpuid(0x00, 0, &eax, &ebx, &ecx, &edx); 147 if (eax < 0x1A) 148 return false; 149 150 cpuid(0x07, 0, &eax, &ebx, &ecx, &edx); 151 if (!(edx & (1 << 15))) 152 return false; 153 154 return true; 155} 156 157static void enumerate_hybrid_cores(icepick_topology_t *topo) 158{ 159 int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); 160 if (num_cpus <= 0) 161 return; 162 163 cpu_set_t original_affinity; 164 sched_getaffinity(0, sizeof(original_affinity), &original_affinity); 165 166 topo->pcore_count = 0; 167 topo->ecore_count = 0; 168 169 for (int cpu = 0; cpu < num_cpus && cpu < 320; cpu++) { 170 cpu_set_t cpuset; 171 CPU_ZERO(&cpuset); 172 CPU_SET(cpu, &cpuset); 173 174 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 175 continue; 176 177 uint32_t eax, ebx, ecx, edx; 178 cpuid(0x1A, 0, &eax, &ebx, &ecx, &edx); 179 180 uint32_t core_type = (eax >> 24) & 0xFF; 181 182 if (core_type == 0x40 && topo->pcore_count < 64) { 183 topo->pcore_cpus[topo->pcore_count++] = cpu; 184 } else if (core_type == 0x20 && topo->ecore_count < 256) { 185 topo->ecore_cpus[topo->ecore_count++] = cpu; 186 } 187 } 188 189 sched_setaffinity(0, sizeof(original_affinity), &original_affinity); 190} 191 192int find_cpu_for_core_type(const icepick_topology_t *topo, icepick_core_type_t core_type, 193 int numa_node) 194{ 195 (void)numa_node; 196 197 if (!topo->is_hybrid || core_type == ICEPICK_CORE_ANY) { 198 int cpu = sched_getcpu(); 199 return cpu >= 0 ? cpu : 0; 200 } 201 202 if (core_type == ICEPICK_CORE_PCORE && topo->pcore_count > 0) 203 return topo->pcore_cpus[0]; 204 205 if (core_type == ICEPICK_CORE_ECORE && topo->ecore_count > 0) 206 return topo->ecore_cpus[0]; 207 208 return 0; 209} 210 211int icepick_discover_topology(icepick_topology_t **topo) 212{ 213 icepick_topology_t *t = calloc(1, sizeof(*t)); 214 if (!t) 215 return ICEPICK_E_ALLOC; 216 217 t->cat_supported = detect_cat_support(t); 218 if (!t->cat_supported) { 219 free(t); 220 return ICEPICK_E_NO_CAT; 221 } 222 223 t->mba_supported = detect_mba_support(t); 224 225 bool is_amd = is_amd_cpu(); 226 if (is_amd) 227 detect_amd_cache_topology(t); 228 else 229 detect_intel_cache_topology(t); 230 231 if (t->l3_size == 0 || t->l3_ways == 0) { 232 t->way_size = 2 * 1024 * 1024; 233 t->l3_size = t->way_size * t->l3_ways; 234 } 235 236 detect_numa_topology(t); 237 238 if (!is_amd) { 239 t->is_hybrid = detect_hybrid_architecture(t); 240 if (t->is_hybrid) 241 enumerate_hybrid_cores(t); 242 } 243 244 *topo = t; 245 return 0; 246} 247 248void icepick_free_topology(icepick_topology_t *topo) 249{ 250 free(topo); 251} 252 253unsigned icepick_topology_l3_ways(const icepick_topology_t *topo) 254{ 255 return topo->l3_ways; 256} 257 258size_t icepick_topology_l3_size(const icepick_topology_t *topo) 259{ 260 return topo->l3_size; 261} 262 263size_t icepick_topology_way_size(const icepick_topology_t *topo) 264{ 265 return topo->way_size; 266} 267 268unsigned icepick_topology_max_clos(const icepick_topology_t *topo) 269{ 270 return topo->max_clos; 271} 272 273unsigned icepick_topology_ccx_count(const icepick_topology_t *topo) 274{ 275 return topo->ccx_count; 276} 277 278bool icepick_topology_cat_supported(const icepick_topology_t *topo) 279{ 280 return topo->cat_supported; 281} 282 283bool icepick_topology_mba_supported(const icepick_topology_t *topo) 284{ 285 return topo->mba_supported; 286} 287 288bool icepick_topology_mba_is_linear(const icepick_topology_t *topo) 289{ 290 return topo->mba_is_linear; 291} 292 293unsigned icepick_topology_max_mba_thrtl(const icepick_topology_t *topo) 294{ 295 return topo->max_mba_thrtl; 296} 297 298bool icepick_topology_is_hybrid(const icepick_topology_t *topo) 299{ 300 return topo->is_hybrid; 301} 302 303unsigned icepick_topology_pcore_count(const icepick_topology_t *topo) 304{ 305 return topo->pcore_count; 306} 307 308unsigned icepick_topology_ecore_count(const icepick_topology_t *topo) 309{ 310 return topo->ecore_count; 311}