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