2660a0b2d6800cbd5939b8c8e18428036b7d51ee
[barrelfish] / lib / numa / utilities.c
1 /**
2  * \file
3  * \brief General Numa functions
4  *
5  */
6
7 /*
8  * Copyright (c) 2014, ETH Zurich.
9  * All rights reserved.
10  *
11  * This file is distributed under the terms in the attached LICENSE file.
12  * If you do not find this file, copies can be found by writing to:
13  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
14  */
15
16 #include <stdio.h>
17 #include <string.h>
18
19 #include <barrelfish/barrelfish.h>
20 #include <skb/skb.h>
21
22 #include <numa.h>
23
24 #include "numa_internal.h"
25
26 /**
27  * \brief dumps the numa topology structure
28  *
29  * \param topology pointer to the topology to dump
30  */
31 void numa_dump_topology(struct numa_topology *topology)
32 {
33     if (topology->nodes == NULL) {
34         printf("NUMA TOPOLOGY INVALID\n");
35         return;
36     }
37
38     printf("dumping NUMA topology\n");
39     printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");
40
41     printf("Cores: %" PRIuCOREID "  Nodes: %" PRIuNODEID" \n", topology->num_cores,
42            topology->num_nodes);
43
44     printf("---------------------------------------\n");
45     for (nodeid_t nodeid = 0; nodeid < topology->num_nodes; ++nodeid) {
46         struct numa_node *node = &topology->nodes[nodeid];
47         printf(" # Node %" PRIuNODEID ":  [0x%016" PRIxLPADDR ", 0x%016" PRIxLPADDR
48                "] of %" PRIu64 " MB\n", nodeid, node->mem_base, node->mem_limit,
49                (node->mem_limit - node->mem_base) >> 20);
50         for (coreid_t coreid = 0; coreid < node->num_cores; ++coreid) {
51             struct numa_core *core = &node->cores[coreid];
52             printf("  + Core %-3" PRIuCOREID ": [apic=%-3" PRIu16 ", node=%-3"
53                         PRIuNODEID "]\n", core->id, core->apicid, core->node->id);
54         }
55     }
56
57     printf("---------------------------------------\n");
58     for (coreid_t coreid = 0; coreid < topology->num_cores; ++coreid) {
59         struct numa_core *core = topology->cores[coreid];
60         printf(" # Core %-3" PRIuCOREID ": [apic=%-3" PRIu16 ", node=%-3"
61                PRIuNODEID "]\n", coreid, core->apicid, core->node->id);
62     }
63
64     printf("---------------------------------------\n");
65     printf("Locality map:\n");
66     printf("     ");
67     for (coreid_t node_from = 0; node_from < topology->num_nodes; ++node_from) {
68         printf("%02" PRIuNODEID " ", node_from);
69     }
70     printf("\n");
71     for (coreid_t node_from = 0; node_from < topology->num_nodes; ++node_from) {
72         printf("  %02" PRIuNODEID  " ", node_from);
73         for (coreid_t node_to = 0; node_to < topology->num_nodes; ++node_to) {
74             printf("%02" PRIu32 " ",
75                    topology->distances[node_from * topology->num_nodes + node_to]);
76         }
77         printf("\n");
78     }
79
80     printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
81 }
82
83 /**
84  * \brief obtains the system topology from the SKB
85  *
86  * \param topology pointer to the topology information structure
87  *
88  * \returns SYS_ERR_OK on SUCCESS
89  *          errval on FAILURE
90  */
91 errval_t numa_get_topology_from_skb(struct numa_topology *topology)
92 {
93     errval_t err;
94
95     /* don't query if no return pointer is specified */
96     if (topology == NULL) {
97         return SYS_ERR_OK;
98     }
99
100     NUMA_DEBUG_INIT("getting topology from SKB...\n");
101
102     err = skb_client_connect();
103     if (err_is_fail(err)) {
104         return err_push(err, NUMA_ERR_SKB);
105     }
106
107     err = skb_execute_query("get_system_topology(Nnodes,Ncores,Lnodes,Lcores,Llocalities),"
108                             "writeln(num(nodes(Nnodes),cores(Ncores))),"
109                             "writeln(Lnodes),writeln(Lcores), writeln(Llocalities).");
110     if (err_is_fail(err)) {
111         DEBUG_ERR(err, "skb query failed");
112         return err_push(err, NUMA_ERR_SKB);
113     }
114
115     uint32_t core = 0;
116     uint32_t node = 0;
117     err = skb_read_output("num(nodes(%d), cores(%d))", &node, &core);
118     if (err_is_fail(err)) {
119         return err_push(err, NUMA_ERR_SKB_DATA);
120     }
121
122     NUMA_DEBUG_INIT("discovered topology with %" PRIuNODEID " nodes, %" PRIuCOREID
123                     " cores\n", node, core);
124
125     if (!core || !node || node > core) {
126         NUMA_ERROR("invalid number of cores %" PRIu32 " or nodes %" PRIu32 ".",
127                    core, node);
128         return err_push(err, NUMA_ERR_SKB_DATA);
129     }
130
131     if (core >= MAX_COREID || node >= NUMA_MAX_NUMNODES) {
132         NUMA_ERROR("too large number of cores %" PRIu32 " or nodes %" PRIu32 ".",
133                    core, node);
134         return err_push(err, NUMA_ERR_SKB_DATA);
135     }
136
137     topology->num_cores = (coreid_t) core;
138     topology->num_nodes = (nodeid_t) node;
139
140     topology->preferred = NUMA_POLICY_DEFAULT;
141     topology->strict = NUMA_POLICY_DEFAULT;
142
143     topology->nodes = malloc(node * sizeof(struct numa_node)
144                                 + node * node * sizeof(uint32_t)
145                                 + core * sizeof(struct numa_core)
146                                 + core * sizeof(void *));
147     if (topology->nodes == NULL) {
148         return LIB_ERR_MALLOC_FAIL;
149     }
150
151     struct numa_core *cores_array = (struct numa_core *)(topology->nodes + node);
152
153     topology->cores = (struct numa_core **) (cores_array + core);
154     topology->distances = (uint32_t*)(topology->cores + core);
155
156     /* skip over the initial node and core information */
157     char *output = strchr(skb_get_output(), '\n') + 1;
158     uint32_t parsed = 0;
159
160     /* read the node list */
161     struct list_parser_status parser;
162     skb_read_list_init_offset(&parser, output, 0);
163
164     lpaddr_t base, limit;
165
166     NUMA_DEBUG_INIT("parsing node information...\n");
167     while (skb_read_list(&parser, "node(%" PRIu32 ", %" PRIuLPADDR ", %" PRIuLPADDR ")",
168                          &node, &base, &limit)) {
169         if (parsed == topology->num_nodes) {
170             parsed++;
171             break;
172         }
173
174         // XXX: assume the IDs are labelled 0..n-1
175         assert(parsed == node);
176
177         topology->nodes[parsed].num_cores = 0;
178         topology->nodes[parsed].id = node;
179         topology->nodes[parsed].mem_base = base;
180         topology->nodes[parsed].mem_limit = limit;
181         topology->nodes[parsed].apicid = (uint16_t) -1;
182         topology->nodes[parsed].cores = NULL;
183
184         // TODO: topology->nodes[node].coresbm = allocbm()
185
186         NUMA_DEBUG_INIT("  > node %" PRIuNODEID " [0x%016" PRIxLPADDR", 0x%016"
187                         PRIxLPADDR "] (%" PRIuLPADDR " MB)\n",
188                         node, base, limit, (limit-base) >> 20);
189         parsed++;
190     }
191
192     if ((nodeid_t) parsed != topology->num_nodes) {
193         NUMA_DEBUG_INIT("node list incomplete: %" PRIuNODEID ", %" PRIuNODEID "\n",
194                         parsed, topology->num_nodes);
195         err = NUMA_ERR_SKB_DATA;
196         goto error_out;
197     }
198
199     char arch[10];
200     uint32_t apic = 0;
201
202     /* parse the numa core list */
203     output = strchr(output, '\n') + 1;
204     skb_read_list_init_offset(&parser, output, 0);
205     parsed = 0;
206
207     NUMA_DEBUG_INIT("parsing core information...\n");
208     while (skb_read_list(&parser, "cpu(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ", %[^,)] , "
209                          "dummy)", &node, &core, &apic,arch)) {
210         if (parsed == topology->num_cores) {
211             parsed++;
212             break;
213         }
214         if (!(node < topology->num_nodes)) {
215             NUMA_DEBUG_INIT("core %" PRIuCOREID " invalid node id %" PRIuNODEID "\n",
216                             core, node);
217             err = NUMA_ERR_SKB_DATA;
218             goto error_out;
219         }
220
221         topology->nodes[node].num_cores++;
222
223         /* the cores come sorted by nodes. The first one sets the cores pointer*/
224         if (topology->nodes[node].cores == NULL) {
225             topology->nodes[node].cores = &cores_array[parsed];
226         }
227
228         // TODO:  set bitmask topology->nodes[node].coresbm
229         cores_array[parsed].id = (coreid_t) core;
230         cores_array[parsed].apicid = (uint16_t) apic;
231         cores_array[parsed].node = &topology->nodes[node];
232         cores_array[parsed].arch = archstr_to_cputype(arch);
233
234         // set the entry in the cores array
235         topology->cores[core] = &cores_array[parsed];
236
237         if (cores_array[parsed].arch == CPU_TYPE_NUM) {
238             err = SYS_ERR_ARCHITECTURE_NOT_SUPPORTED;
239             goto error_out;
240         }
241
242         NUMA_DEBUG_INIT("  > %s core %" PRIuCOREID" apic=%" PRIu32 ", node=%"
243                         PRIuNODEID "\n", arch, (coreid_t )core, apic, node);
244         parsed++;
245     }
246
247     if ((coreid_t) parsed != topology->num_cores) {
248         NUMA_DEBUG_INIT("core list incomplete: %" PRIuCOREID ", %" PRIuCOREID "\n",
249                         (coreid_t )parsed, topology->num_cores);
250         err = NUMA_ERR_SKB_DATA;
251         goto error_out;
252     }
253
254     output = strchr(output, '\n') + 1;
255     skb_read_list_init_offset(&parser, output, 0);
256     parsed = 0;
257
258     uint32_t from, to;
259     uint32_t distance;
260     NUMA_DEBUG_INIT("parsing locality information...\n");
261     while (skb_read_list(&parser, "node_distance(%" PRIuNODEID ", %" PRIuNODEID ", %" PRIu32 ")",
262                           &from, &to, &distance)) {\
263         NUMA_DEBUG_INIT("  > [%" PRIuNODEID "] -> [%" PRIuNODEID "] = %" PRIu32"\n",
264                         from, to, distance);
265         topology->distances[from * topology->num_nodes + to] = distance;
266         parsed++;
267     }
268
269     if (parsed == 0) {
270         NUMA_DEBUG_INIT("locality list not existent setting all to 10...\n");
271         for (nodeid_t i = 0; i < topology->num_nodes; ++i) {
272             for (nodeid_t j = 0; j < topology->num_nodes; ++j) {
273                 topology->distances[from * topology->num_nodes + to] = 10;
274             }
275         }
276     } else if (parsed != (uint32_t)topology->num_nodes * topology->num_nodes) {
277         NUMA_DEBUG_INIT("locality list incomplete: %" PRIu32" / %" PRIu32 "\n",
278                         parsed, (uint32_t)topology->num_nodes * topology->num_nodes);
279         err = NUMA_ERR_SKB_DATA;
280         goto error_out;
281     }
282
283     return SYS_ERR_OK;
284
285     error_out:
286     free(topology->nodes);
287     return err;
288 }
289
290 /**
291  * \brief frees the numa topology structure
292  *
293  * \param topology pointer to the topology information structure
294  */
295 void numa_free_topology(struct numa_topology *topology)
296 {
297     if (topology && topology->nodes) {
298         free(topology->nodes);
299     }
300     memset(topology, 0, sizeof(*topology));
301 }