1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <lib/system-topology.h>
6 #include <unittest/unittest.h>
7
8 namespace {
9
10 using system_topology::Node;
11
12 // Should be larger than the largest topology used here.
13 constexpr size_t kTopologyArraySize = 60;
14
15 struct FlatTopo {
16 zbi_topology_node_t nodes[kTopologyArraySize];
17 size_t node_count = 0;
18 };
19
20 // Defined at bottom of file, they are long and noisy.
21
22 // A generic arm big.LITTLE.
23 FlatTopo SimpleTopology();
24
25 // Roughly a threadripper 2990X.
26 FlatTopo ComplexTopology();
27
28 // Same as Simple but stored with all nodes on a level adjacent to each other.
29 FlatTopo HierarchicalTopology();
30
test_flat_to_heap_simple()31 bool test_flat_to_heap_simple() {
32 BEGIN_TEST;
33 FlatTopo topo = SimpleTopology();
34
35 system_topology::Graph graph;
36 ASSERT_EQ(ZX_OK, graph.Update(topo.nodes, topo.node_count));
37 ASSERT_EQ(3, graph.processors().size());
38
39 // Test lookup.
40 system_topology::Node* node;
41 ASSERT_EQ(ZX_OK, graph.ProcessorByLogicalId(1, &node));
42 ASSERT_EQ(ZBI_TOPOLOGY_ENTITY_PROCESSOR, node->entity_type);
43 ASSERT_EQ(ZBI_TOPOLOGY_PROCESSOR_PRIMARY, node->entity.processor.flags);
44 ASSERT_EQ(ZBI_TOPOLOGY_ENTITY_CLUSTER, node->parent->entity_type);
45 ASSERT_EQ(1, node->parent->entity.cluster.performance_class);
46
47 END_TEST;
48 }
49
test_flat_to_heap_complex()50 bool test_flat_to_heap_complex() {
51 BEGIN_TEST;
52 FlatTopo topo = ComplexTopology();
53
54 system_topology::Graph graph;
55 ASSERT_EQ(ZX_OK, graph.Update(topo.nodes, topo.node_count));
56 ASSERT_EQ(32, graph.processors().size());
57
58 END_TEST;
59 }
60
test_flat_to_heap_walk_result()61 bool test_flat_to_heap_walk_result() {
62 BEGIN_TEST;
63 FlatTopo topo = ComplexTopology();
64
65 system_topology::Graph graph;
66 ASSERT_EQ(ZX_OK, graph.Update(topo.nodes, topo.node_count));
67 ASSERT_EQ(32, graph.processors().size());
68
69 // For each processor we walk all the way up the graph.
70 for (Node* processor : graph.processors()) {
71 Node* current = processor;
72 Node* next = current->parent;
73 while (next != nullptr) {
74 // Ensure that the children lists contain all children.
75 bool found = false;
76 for (Node* child : next->children) {
77 found |= child == current;
78 }
79 ASSERT_TRUE(found,
80 "A node is not listed as a child of its parent.");
81
82 current = current->parent;
83 next = current->parent;
84 }
85 }
86
87 END_TEST;
88 }
89
test_validate_processor_not_leaf()90 bool test_validate_processor_not_leaf() {
91 BEGIN_TEST;
92 FlatTopo topo = ComplexTopology();
93
94 // Replace a die node with a processor.
95 topo.nodes[1].entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR;
96
97 system_topology::Graph graph;
98 ASSERT_EQ(ZX_ERR_INVALID_ARGS, graph.Update(topo.nodes, topo.node_count));
99
100 END_TEST;
101 }
102
test_validate_leaf_not_processor()103 bool test_validate_leaf_not_processor() {
104 BEGIN_TEST;
105 FlatTopo topo = SimpleTopology();
106
107 // Replace a processor node with a cluster.
108 topo.nodes[4].entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER;
109
110 system_topology::Graph graph;
111 ASSERT_EQ(ZX_ERR_INVALID_ARGS, graph.Update(topo.nodes, topo.node_count));
112
113 END_TEST;
114 }
115
test_validate_cycle()116 bool test_validate_cycle() {
117 BEGIN_TEST;
118 FlatTopo topo = ComplexTopology();
119
120 // Set the parent index of the die to a processor under it.
121 topo.nodes[1].parent_index = 4;
122
123 system_topology::Graph graph;
124 ASSERT_EQ(ZX_ERR_INVALID_ARGS, graph.Update(topo.nodes, topo.node_count));
125
126 END_TEST;
127 }
128
129 // This is a cycle like above but fails due to parent mismatch with other nodes
130 // on its level.
test_validate_cycle_shared_parent()131 bool test_validate_cycle_shared_parent() {
132 BEGIN_TEST;
133 FlatTopo topo = ComplexTopology();
134
135 // Set the parent index of the die to a processor under it.
136 topo.nodes[2].parent_index = 4;
137
138 system_topology::Graph graph;
139 ASSERT_EQ(ZX_ERR_INVALID_ARGS, graph.Update(topo.nodes, topo.node_count));
140
141 END_TEST;
142 }
143
144 // Another logical way to store the graph would be hierarchical, all the top
145 // level nodes together, followed by the next level, and so on.
146 // We are proscriptive however that they should be stored in a depth-first
147 // ordering, so this other ordering should fail validation.
test_validate_hierarchical_storage()148 bool test_validate_hierarchical_storage() {
149 BEGIN_TEST;
150 FlatTopo topo = HierarchicalTopology();
151
152 // Set the parent index of the die to a processor under it.
153 topo.nodes[2].parent_index = 4;
154
155 system_topology::Graph graph;
156 ASSERT_EQ(ZX_ERR_INVALID_ARGS, graph.Update(topo.nodes, topo.node_count));
157
158 END_TEST;
159 }
160
161 BEGIN_TEST_CASE(system_topology_parsing_tests)
RUN_TEST(test_flat_to_heap_simple)162 RUN_TEST(test_flat_to_heap_simple)
163 RUN_TEST(test_flat_to_heap_complex)
164 RUN_TEST(test_flat_to_heap_walk_result)
165 END_TEST_CASE(system_topology_parsing_tests)
166
167 BEGIN_TEST_CASE(system_topology_validation_tests)
168 RUN_TEST(test_validate_processor_not_leaf)
169 RUN_TEST(test_validate_leaf_not_processor)
170 RUN_TEST(test_validate_cycle)
171 RUN_TEST(test_validate_cycle_shared_parent)
172 RUN_TEST(test_validate_hierarchical_storage)
173 END_TEST_CASE(system_topology_validation_tests)
174
175 // Generic ARM big.LITTLE layout.
176 // [cluster] [cluster]
177 // [p1] [p3] [p4]
178 FlatTopo SimpleTopology() {
179 FlatTopo topo;
180 zbi_topology_node_t* nodes = topo.nodes;
181
182 uint16_t logical_processor = 0;
183 uint16_t big_cluster = 0, little_cluster = 0;
184
185 size_t index = 0;
186 nodes[big_cluster = index++] = (zbi_topology_node_t){
187 .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
188 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
189 .entity = {
190 .cluster = {
191 .performance_class = 1,
192 }}};
193
194 nodes[index++] = (zbi_topology_node_t){
195 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
196 .parent_index = big_cluster,
197 .entity = {
198 .processor = {
199 .logical_ids = {logical_processor++, logical_processor++},
200 .logical_id_count = 2,
201 .flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
202 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
203 }}};
204
205 nodes[little_cluster = index++] = (zbi_topology_node_t){
206 .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
207 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
208 .entity = {
209 .cluster = {
210 .performance_class = 0,
211 }}};
212
213 nodes[index++] = (zbi_topology_node_t){
214 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
215 .parent_index = little_cluster,
216 .entity = {
217 .processor = {
218 .logical_ids = {logical_processor++},
219 .logical_id_count = 1,
220 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
221 }}};
222
223 nodes[index++] = (zbi_topology_node_t){
224 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
225 .parent_index = little_cluster,
226 .entity = {
227 .processor = {
228 .logical_ids = {logical_processor++},
229 .logical_id_count = 1,
230 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
231 }}};
232
233 EXPECT_LT(index, kTopologyArraySize);
234 topo.node_count = index;
235 return topo;
236 }
237
HierarchicalTopology()238 FlatTopo HierarchicalTopology() {
239 FlatTopo topo;
240 zbi_topology_node_t* nodes = topo.nodes;
241
242 uint16_t logical_processor = 0;
243 uint16_t big_cluster = 0, little_cluster = 0;
244
245 size_t index = 0;
246 nodes[big_cluster = index++] = (zbi_topology_node_t){
247 .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
248 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
249 .entity = {
250 .cluster = {
251 .performance_class = 1,
252 }}};
253
254 nodes[little_cluster = index++] = (zbi_topology_node_t){
255 .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
256 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
257 .entity = {
258 .cluster = {
259 .performance_class = 0,
260 }}};
261
262 nodes[index++] = (zbi_topology_node_t){
263 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
264 .parent_index = big_cluster,
265 .entity = {
266 .processor = {
267 .logical_ids = {logical_processor++, logical_processor++},
268 .logical_id_count = 2,
269 .flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
270 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
271 }}};
272
273 nodes[index++] = (zbi_topology_node_t){
274 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
275 .parent_index = little_cluster,
276 .entity = {
277 .processor = {
278 .logical_ids = {logical_processor++},
279 .logical_id_count = 1,
280 .flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
281 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
282 }}};
283
284 nodes[index++] = (zbi_topology_node_t){
285 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
286 .parent_index = little_cluster,
287 .entity = {
288 .processor = {
289 .logical_ids = {logical_processor++},
290 .logical_id_count = 1,
291 .flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
292 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
293 }}};
294
295 EXPECT_LT(index, kTopologyArraySize);
296 topo.node_count = index;
297 return topo;
298 }
299
300 // Add a threadripper CCX (CPU complex), a four core cluster.
AddCCX(uint16_t parent,zbi_topology_node_t * nodes,size_t * index,uint16_t * logical_processor)301 void AddCCX(uint16_t parent, zbi_topology_node_t* nodes,
302 size_t* index, uint16_t* logical_processor) {
303 uint16_t cache = 0, cluster = 0;
304 nodes[cluster = (*index)++] = (zbi_topology_node_t){
305 .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
306 .parent_index = parent,
307 .entity = {
308 .cluster = {
309 .performance_class = 0,
310 }}};
311
312 nodes[cache = (*index)++] = (zbi_topology_node_t){
313 .entity_type = ZBI_TOPOLOGY_ENTITY_CACHE,
314 .parent_index = cluster,
315 };
316
317 for (int i = 0; i < 4; i++) {
318 nodes[(*index)++] = (zbi_topology_node_t){
319 .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
320 .parent_index = cache,
321 .entity = {
322 .processor = {
323 .logical_ids = {(*logical_processor)++, (*logical_processor)++},
324 .logical_id_count = 2,
325 .architecture = ZBI_TOPOLOGY_ARCH_UNDEFINED,
326 }}};
327 }
328 }
329
330 // Roughly a threadripper 2990X.
331 // Four sets of the following:
332 // [numa1]
333 // [die1]
334 // [cluster1] [cluster2]
335 // [cache1] [cache2]
336 // [p1][p2][p3][p4] [p5][p6][p7][p8]
ComplexTopology()337 FlatTopo ComplexTopology() {
338 FlatTopo topo;
339 zbi_topology_node_t* nodes = topo.nodes;
340
341 uint16_t logical_processor = 0;
342 uint16_t die[4] = {0};
343 uint16_t numa[4] = {0};
344
345 size_t index = 0;
346
347 nodes[numa[0] = index++] = (zbi_topology_node_t){
348 .entity_type = ZBI_TOPOLOGY_ENTITY_NUMA_REGION,
349 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
350 .entity = {
351 .numa_region = {
352 .start_address = 0x1,
353 .end_address = 0x2,
354 }}};
355
356 nodes[die[0] = index++] = (zbi_topology_node_t){
357 .entity_type = ZBI_TOPOLOGY_ENTITY_DIE,
358 .parent_index = numa[0],
359 };
360
361 AddCCX(die[0], nodes, &index, &logical_processor);
362 AddCCX(die[0], nodes, &index, &logical_processor);
363
364 nodes[numa[1] = index++] = (zbi_topology_node_t){
365 .entity_type = ZBI_TOPOLOGY_ENTITY_NUMA_REGION,
366 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
367 .entity = {
368 .numa_region = {
369 .start_address = 0x3,
370 .end_address = 0x4,
371 }}};
372
373 nodes[die[1] = index++] = (zbi_topology_node_t){
374 .entity_type = ZBI_TOPOLOGY_ENTITY_DIE,
375 .parent_index = numa[1],
376 };
377
378 AddCCX(die[1], nodes, &index, &logical_processor);
379 AddCCX(die[1], nodes, &index, &logical_processor);
380
381 nodes[numa[2] = index++] = (zbi_topology_node_t){
382 .entity_type = ZBI_TOPOLOGY_ENTITY_NUMA_REGION,
383 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
384 .entity = {
385 .numa_region = {
386 .start_address = 0x5,
387 .end_address = 0x6,
388 }}};
389
390 nodes[die[2] = index++] = (zbi_topology_node_t){
391 .entity_type = ZBI_TOPOLOGY_ENTITY_DIE,
392 .parent_index = numa[2],
393 };
394
395 AddCCX(die[2], nodes, &index, &logical_processor);
396 AddCCX(die[2], nodes, &index, &logical_processor);
397
398 nodes[numa[3] = index++] = (zbi_topology_node_t){
399 .entity_type = ZBI_TOPOLOGY_ENTITY_NUMA_REGION,
400 .parent_index = ZBI_TOPOLOGY_NO_PARENT,
401 .entity = {
402 .numa_region = {
403 .start_address = 0x7,
404 .end_address = 0x8,
405 }}};
406
407 nodes[die[3] = index++] = (zbi_topology_node_t){
408 .entity_type = ZBI_TOPOLOGY_ENTITY_DIE,
409 .parent_index = numa[3],
410 };
411
412 AddCCX(die[3], nodes, &index, &logical_processor);
413 AddCCX(die[3], nodes, &index, &logical_processor);
414
415 EXPECT_LT(index, kTopologyArraySize);
416 topo.node_count = index;
417 return topo;
418 }
419
420 } // namespace
421
main(int argc,char ** argv)422 int main(int argc, char** argv) {
423 return unittest_run_all_tests(argc, argv) ? 0 : -1;
424 }
425