cache line locking on AMD x86_64 utilising L3 CAT pseudo-locking
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
10static void cpuid(uint32_t leaf, uint32_t subleaf,
11 uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
12{
13 __cpuid_count(leaf, subleaf, *eax, *ebx, *ecx, *edx);
14}
15
16static bool detect_cat_support(icepick_topology_t *topo)
17{
18 uint32_t eax, ebx, ecx, edx;
19
20 cpuid(0x10, 0, &eax, &ebx, &ecx, &edx);
21 if (!(ebx & (1 << 1)))
22 return false;
23
24 cpuid(0x10, 1, &eax, &ebx, &ecx, &edx);
25
26 topo->l3_ways = (eax & 0x1F) + 1;
27 topo->max_clos = (edx & 0xFFFF) + 1;
28 topo->default_way_mask = ebx;
29
30 if (topo->max_clos > MAX_CLOS)
31 topo->max_clos = MAX_CLOS;
32
33 return true;
34}
35
36static void detect_amd_cache_topology(icepick_topology_t *topo)
37{
38 uint32_t eax, ebx, ecx, edx;
39
40 for (int i = 0; i < 16; i++) {
41 cpuid(0x8000001D, i, &eax, &ebx, &ecx, &edx);
42
43 uint32_t cache_type = eax & 0x1F;
44 if (cache_type == 0)
45 break;
46
47 uint32_t cache_level = (eax >> 5) & 0x7;
48 if (cache_level != 3)
49 continue;
50
51 uint32_t line_size = (ebx & 0xFFF) + 1;
52 uint32_t partitions = ((ebx >> 12) & 0x3FF) + 1;
53 uint32_t ways = ((ebx >> 22) & 0x3FF) + 1;
54 uint32_t sets = ecx + 1;
55
56 topo->l3_size = (size_t)line_size * partitions * ways * sets;
57 topo->l3_ways = ways;
58 topo->way_size = topo->l3_size / topo->l3_ways;
59 break;
60 }
61}
62
63static void detect_intel_cache_topology(icepick_topology_t *topo)
64{
65 uint32_t eax, ebx, ecx, edx;
66
67 for (int i = 0; i < 16; i++) {
68 cpuid(0x04, i, &eax, &ebx, &ecx, &edx);
69
70 uint32_t cache_type = eax & 0x1F;
71 if (cache_type == 0)
72 break;
73
74 uint32_t cache_level = (eax >> 5) & 0x7;
75 if (cache_level != 3)
76 continue;
77
78 uint32_t line_size = (ebx & 0xFFF) + 1;
79 uint32_t partitions = ((ebx >> 12) & 0x3FF) + 1;
80 uint32_t ways = ((ebx >> 22) & 0x3FF) + 1;
81 uint32_t sets = ecx + 1;
82
83 topo->l3_size = (size_t)line_size * partitions * ways * sets;
84 topo->l3_ways = ways;
85 topo->way_size = topo->l3_size / topo->l3_ways;
86 break;
87 }
88}
89
90static bool is_amd_cpu(void)
91{
92 uint32_t eax, ebx, ecx, edx;
93 cpuid(0, 0, &eax, &ebx, &ecx, &edx);
94 return (ebx == 0x68747541 && edx == 0x69746E65 && ecx == 0x444D4163);
95}
96
97static int count_ccxs(void)
98{
99 DIR *dir = opendir("/sys/devices/system/node");
100 if (!dir)
101 return 1;
102
103 int count = 0;
104 struct dirent *entry;
105 while ((entry = readdir(dir))) {
106 if (strncmp(entry->d_name, "node", 4) == 0)
107 count++;
108 }
109 closedir(dir);
110
111 return count > 0 ? count : 1;
112}
113
114static void detect_numa_topology(icepick_topology_t *topo)
115{
116 topo->ccx_count = count_ccxs();
117 if (topo->ccx_count > MAX_CCXS)
118 topo->ccx_count = MAX_CCXS;
119
120 for (unsigned i = 0; i < topo->ccx_count; i++)
121 topo->ccx_numa_node[i] = i;
122}
123
124int icepick_discover_topology(icepick_topology_t **topo)
125{
126 icepick_topology_t *t = calloc(1, sizeof(*t));
127 if (!t)
128 return ICEPICK_E_ALLOC;
129
130 t->cat_supported = detect_cat_support(t);
131 if (!t->cat_supported) {
132 free(t);
133 return ICEPICK_E_NO_CAT;
134 }
135
136 if (is_amd_cpu())
137 detect_amd_cache_topology(t);
138 else
139 detect_intel_cache_topology(t);
140
141 if (t->l3_size == 0 || t->l3_ways == 0) {
142 t->way_size = 2 * 1024 * 1024;
143 t->l3_size = t->way_size * t->l3_ways;
144 }
145
146 detect_numa_topology(t);
147
148 *topo = t;
149 return 0;
150}
151
152void icepick_free_topology(icepick_topology_t *topo)
153{
154 free(topo);
155}
156
157unsigned icepick_topology_l3_ways(const icepick_topology_t *topo)
158{
159 return topo->l3_ways;
160}
161
162size_t icepick_topology_l3_size(const icepick_topology_t *topo)
163{
164 return topo->l3_size;
165}
166
167size_t icepick_topology_way_size(const icepick_topology_t *topo)
168{
169 return topo->way_size;
170}
171
172unsigned icepick_topology_max_clos(const icepick_topology_t *topo)
173{
174 return topo->max_clos;
175}
176
177unsigned icepick_topology_ccx_count(const icepick_topology_t *topo)
178{
179 return topo->ccx_count;
180}
181
182bool icepick_topology_cat_supported(const icepick_topology_t *topo)
183{
184 return topo->cat_supported;
185}