Added perfmon interrupt handling to allow overflow based profiling
[akaros.git] / kern / arch / x86 / topology.c
1 /* Copyright (c) 2015 The Regents of the University of California
2  * Kevin Klues <klueska@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stddef.h>
8 #include <kmalloc.h>
9 #include <string.h>
10 #include <acpi.h>
11 #include <arch/arch.h>
12 #include <arch/apic.h>
13 #include <arch/topology.h>
14
15 struct topology_info cpu_topology_info;
16 int *os_coreid_lookup;
17
18 #define num_cpus            (cpu_topology_info.num_cpus)
19 #define num_sockets         (cpu_topology_info.num_sockets)
20 #define num_numa            (cpu_topology_info.num_numa)
21 #define cores_per_numa      (cpu_topology_info.cores_per_numa)
22 #define cores_per_socket    (cpu_topology_info.cores_per_socket)
23 #define cores_per_cpu       (cpu_topology_info.cores_per_cpu)
24 #define cpus_per_socket     (cpu_topology_info.cpus_per_socket)
25 #define cpus_per_numa       (cpu_topology_info.cpus_per_numa)
26 #define sockets_per_numa    (cpu_topology_info.sockets_per_numa)
27 #define max_apic_id         (cpu_topology_info.max_apic_id)
28 #define core_list           (cpu_topology_info.core_list)
29
30 /* Adjust the ids from any given node type to start at 0 and increase from
31  * there. We use the id_offset in the core_list to index the proper field. */
32 static void adjust_ids(int id_offset)
33 {
34         int new_id = 0, old_id = -1;
35         for (int i = 0; i < num_cores; i++) {
36                 for (int j = 0; j < num_cores; j++) {
37                         int *id_field = ((void*)&core_list[j] + id_offset);
38                         if (*id_field >= new_id) {
39                                 if (old_id == -1)
40                                         old_id = *id_field;
41                                 if (old_id == *id_field)
42                                         *id_field = new_id;
43                         }
44                 }
45                 old_id=-1;
46                 new_id++;
47         }
48 }
49
50 /* Set the actual socket id from the raw socket id extracted from cpuid.  This
51  * algorithm is adapted from the algorithm given at
52  * http://wiki.osdev.org/Detecting_CPU_Topology_(80x86) */
53 static void set_socket_ids(void)
54 {
55         int socket_id, raw_socket_id;
56         for (int numa_id = 0; numa_id < num_numa; numa_id++) {
57                 socket_id = 0;
58                 for (int i = 0; i < num_cores; i++) {
59                         if (core_list[i].numa_id == numa_id) {
60                                 if (core_list[i].socket_id == -1) {
61                                         core_list[i].socket_id = socket_id;
62                                         raw_socket_id = core_list[i].raw_socket_id;
63                                         for (int j = i; j < num_cores; j++) {
64                                                 if (core_list[j].numa_id == numa_id) {
65                                                         if (core_list[j].raw_socket_id == raw_socket_id) {
66                                                                 core_list[j].socket_id = socket_id;
67                                                         }
68                                                 }
69                                         }
70                                 }
71                                 socket_id++;
72                         }
73                 }
74         }
75 }
76
77 /* Loop through our Srat table to find a matching numa domain for the given
78  * apid_id. */
79 static int find_numa_domain(int apic_id)
80 {
81         struct Srat *temp = srat;
82         while (temp) {
83                 if (temp->type == SRlapic) {
84                         if (temp->lapic.apic == apic_id)
85                                 return temp->lapic.dom;
86                 }
87                 temp = temp->next;
88         }
89         return -1;
90 }
91
92 /* Figure out the maximum number of cores we actually have and set it in our
93  * cpu_topology_info struct. */
94 static void set_num_cores(void)
95 {
96         struct Apicst *temp = apics->st;
97         while (temp) {
98                 if (temp->type == ASlapic)
99                         num_cores++;
100                 temp = temp->next;
101         }
102 }
103
104 /* Determine if srat has a unique numa domain compared to to all of the srat
105  * records in list_head that are of type SRlapic. */
106 static bool is_unique_numa(struct Srat *srat, struct Srat *list_head)
107 {
108         while (list_head) {
109                 if (list_head->type == SRlapic) {
110                         if (srat->lapic.dom == list_head->lapic.dom)
111                                 return FALSE;
112                 }
113                 list_head = list_head->next;
114         }
115         return TRUE;
116 }
117
118 /* Figure out the maximum number of numa domains we actually have.
119  * This code should always return >= 0 domains. */
120 static int get_num_numa(void)
121 {
122         int numa = 0;
123         struct Srat *temp = srat;
124         while (temp) {
125                 if (temp->type == SRlapic)
126                         if (is_unique_numa(temp, temp->next))
127                                 numa++;
128                 temp = temp->next;
129         }
130         return numa;
131 }
132
133 /* Set num_numa in our topology struct */
134 static void set_num_numa(void)
135 {
136         num_numa = get_num_numa();
137 }
138
139 /* Figure out what the max apic_id we will ever have is and set it in our
140  * cpu_topology_info struct. */
141 static void set_max_apic_id(void)
142 {
143         struct Apicst *temp = apics->st;
144         while (temp) {
145                 if (temp->type == ASlapic) {
146                         if (temp->lapic.id > max_apic_id)
147                                 max_apic_id = temp->lapic.id;
148                 }
149                 temp = temp->next;
150         }
151 }
152
153 static void init_os_coreid_lookup(void)
154 {
155         /* Allocate (max_apic_id+1) entries in our os_coreid_lookup table.
156          * There may be holes in this table because of the way apic_ids work, but
157          * a little wasted space is OK for a constant time lookup of apic_id ->
158          * logical core id (from the OS's perspective). Memset the array to -1 to
159          * to represent invalid entries (which it's very possible we might have if
160          * the apic_id space has holes in it).  */
161         os_coreid_lookup = kmalloc((max_apic_id + 1) * sizeof(int), 0);
162         memset(os_coreid_lookup, -1, (max_apic_id + 1) * sizeof(int));
163
164         /* Loop through and set all valid entries to 0 to start with (making them
165          * temporarily valid, but not yet set to the correct value). This step is
166          * necessary because there is no ordering to the linked list we are
167          * pulling these ids from. After this, loop back through and set the
168          * mapping appropriately. */
169         struct Apicst *temp = apics->st;
170         while (temp) {
171                 if (temp->type == ASlapic)
172                         os_coreid_lookup[temp->lapic.id] = 0;
173                 temp = temp->next;
174         }
175         int os_coreid = 0;
176         for (int i = 0; i <= max_apic_id; i++)
177                 if (os_coreid_lookup[i] == 0)
178                         os_coreid_lookup[i] = os_coreid++;
179 }
180
181 static void init_core_list(uint32_t core_bits, uint32_t cpu_bits)
182 {
183         /* Assuming num_cores and max_apic_id have been set, we can allocate our
184          * core_list to the proper size. Initialize all entries to 0s to being
185          * with. */
186         core_list = kzmalloc(num_cores * sizeof(struct core_info), 0);
187
188         /* Loop through all possible apic_ids and fill in the core_list array with
189          * *relative* topology info. We will change this relative info to absolute
190          * info in a future step. As part of this step, we update our
191          * os_coreid_lookup array to contain the proper value. */
192         int os_coreid = 0;
193         int max_cpus = (1 << cpu_bits);
194         int max_cores_per_cpu = (1 << core_bits);
195         int max_logical_cores = (1 << (core_bits + cpu_bits));
196         int raw_socket_id = 0, cpu_id = 0, core_id = 0;
197         for (int apic_id = 0; apic_id <= max_apic_id; apic_id++) {
198                 if (os_coreid_lookup[apic_id] != -1) {
199                         raw_socket_id = apic_id & ~(max_logical_cores - 1);
200                         cpu_id = (apic_id >> core_bits) & (max_cpus - 1);
201                         core_id = apic_id & (max_cores_per_cpu - 1);
202
203                         core_list[os_coreid].numa_id = find_numa_domain(apic_id);
204                         core_list[os_coreid].raw_socket_id = raw_socket_id;
205                         core_list[os_coreid].socket_id = -1;
206                         core_list[os_coreid].cpu_id = cpu_id;
207                         core_list[os_coreid].core_id = core_id;
208                         core_list[os_coreid].apic_id = apic_id;
209                         os_coreid++;
210                 }
211         }
212
213         /* In general, the various id's set in the previous step are all unique in
214          * terms of representing the topology (i.e. all cores under the same socket
215          * have the same socket_id set), but these id's are not necessarily
216          * contiguous, and are only relative to the level of the hierarchy they
217          * exist at (e.g.  cpu_id 4 may exist under *both* socket_id 0 and
218          * socket_id 1). In this step, we squash these id's down so they are
219          * contiguous. In a following step, we will make them all absolute instead
220          * of relative. */
221         adjust_ids(offsetof(struct core_info, numa_id));
222         adjust_ids(offsetof(struct core_info, raw_socket_id));
223         adjust_ids(offsetof(struct core_info, cpu_id));
224         adjust_ids(offsetof(struct core_info, core_id));
225
226         /* We haven't yet set the socket id of each core yet. So far, all we've
227          * extracted is a "raw" socket id from the top bits in our apic id, but we
228          * need to condense these down into something workable for a socket id, per
229          * numa domain. OSDev has an algorithm for doing so
230          * (http://wiki.osdev.org/Detecting_CPU_Topology_%2880x86%29). We adapt it
231          * for our setup. */
232         set_socket_ids();
233 }
234
235 static void init_core_list_flat(void)
236 {
237         /* Assuming num_cores and max_apic_id have been set, we can allocate our
238          * core_list to the proper size. Initialize all entries to 0s to being
239          * with. */
240         core_list = kzmalloc(num_cores * sizeof(struct core_info), 0);
241
242         /* Loop through all possible apic_ids and fill in the core_list array with
243          * flat topology info. */
244         int os_coreid = 0;
245         for (int apic_id = 0; apic_id <= max_apic_id; apic_id++) {
246                 if (os_coreid_lookup[apic_id] != -1) {
247                         core_list[os_coreid].numa_id = 0;
248                         core_list[os_coreid].raw_socket_id = 0;
249                         core_list[os_coreid].socket_id = 0;
250                         core_list[os_coreid].cpu_id = 0;
251                         core_list[os_coreid].core_id = os_coreid;
252                         core_list[os_coreid].apic_id = apic_id;
253                         os_coreid++;
254                 }
255         }
256 }
257
258 static void set_remaining_topology_info(void)
259 {
260         /* Assuming we have our core_list set up with relative topology info, loop
261          * through our core_list and calculate the other statistics that we hold
262          * in our cpu_topology_info struct. */
263         int last_numa = -1, last_socket = -1, last_cpu = -1, last_core = -1;
264         for (int i = 0; i < num_cores; i++) {
265                 if (core_list[i].socket_id > last_socket) {
266                         last_socket = core_list[i].socket_id;
267                         sockets_per_numa++;
268                 }
269                 if (core_list[i].cpu_id > last_cpu) {
270                         last_cpu = core_list[i].cpu_id;
271                         cpus_per_socket++;
272                 }
273                 if (core_list[i].core_id > last_core) {
274                         last_core = core_list[i].core_id;
275                         cores_per_cpu++;
276                 }
277         }
278         cores_per_socket = cpus_per_socket * cores_per_cpu;
279         cores_per_numa = sockets_per_numa * cores_per_socket;
280         cpus_per_numa = sockets_per_numa * cpus_per_socket;
281         num_sockets = sockets_per_numa * num_numa;
282         num_cpus = cpus_per_socket * num_sockets;
283 }
284
285 static void update_core_list_with_absolute_ids(void)
286 {
287         /* Fix up our core_list to have absolute id's at every level. */
288         for (int i = 0; i < num_cores; i++) {
289                 struct core_info *c = &core_list[i];
290                 c->socket_id = num_sockets/num_numa * c->numa_id + c->socket_id;
291                 c->cpu_id = num_cpus/num_sockets * c->socket_id + c->cpu_id;
292                 c->core_id = num_cores/num_cpus * c->cpu_id + c->core_id;
293         }
294 }
295
296 static void build_topology(uint32_t core_bits, uint32_t cpu_bits)
297 {
298         set_num_cores();
299         set_num_numa();
300         set_max_apic_id();
301         init_os_coreid_lookup();
302         init_core_list(core_bits, cpu_bits);
303         set_remaining_topology_info();
304         update_core_list_with_absolute_ids();
305 }
306
307 static void build_flat_topology(void)
308 {
309         set_num_cores();
310         num_numa = 1;
311         set_max_apic_id();
312         init_os_coreid_lookup();
313         init_core_list_flat();
314         set_remaining_topology_info();
315 }
316
317 void topology_init(void)
318 {
319         uint32_t eax, ebx, ecx, edx;
320         int smt_leaf, core_leaf;
321         uint32_t core_bits = 0, cpu_bits = 0;
322
323         eax = 0x0000000b;
324         ecx = 1;
325         cpuid(eax, ecx, &eax, &ebx, &ecx, &edx);
326         core_leaf = (ecx >> 8) & 0x00000002;
327         if (core_leaf == 2) {
328                 cpu_bits = eax;
329                 eax = 0x0000000b;
330                 ecx = 0;
331                 cpuid(eax, ecx, &eax, &ebx, &ecx, &edx);
332                 smt_leaf = (ecx >> 8) & 0x00000001;
333                 if (smt_leaf == 1) {
334                         core_bits = eax;
335                         cpu_bits = cpu_bits - core_bits;
336                 }
337         }
338         /* BIOSes are not strictly required to put NUMA information
339          * into the ACPI table. If there is no information the safest
340          * thing to do is assume it's a non-NUMA system, i.e. flat. */
341         if (cpu_bits && get_num_numa())
342                 build_topology(core_bits, cpu_bits);
343         else
344                 build_flat_topology();
345 }
346
347 void print_cpu_topology()
348 {
349         printk("num_numa: %d, num_sockets: %d, num_cpus: %d, num_cores: %d\n",
350                num_numa, num_sockets, num_cpus, num_cores);
351         for (int i = 0; i < num_cores; i++) {
352                 printk("OScoreid: %3d, HWcoreid: %3d, RawSocketid: %3d, "
353                        "Numa Domain: %3d, Socket: %3d, Cpu: %3d, Core: %3d\n",
354                        i,
355                        core_list[i].apic_id,
356                        core_list[i].numa_id,
357                        core_list[i].raw_socket_id,
358                        core_list[i].socket_id,
359                        core_list[i].cpu_id,
360                        core_list[i].core_id);
361         }
362 }