1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * KUnit tests for the Interconnect framework.
4 *
5 * Copyright (c) 2025 Kuan-Wei Chiu <visitorckw@gmail.com>
6 *
7 * This suite verifies the behavior of the interconnect core, including
8 * topology construction, bandwidth aggregation, and path lifecycle.
9 */
10
11 #include <kunit/platform_device.h>
12 #include <kunit/test.h>
13 #include <linux/interconnect-provider.h>
14 #include <linux/interconnect.h>
15 #include <linux/list.h>
16 #include <linux/module.h>
17 #include <linux/overflow.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
20
21 #include "internal.h"
22
23 enum {
24 NODE_CPU,
25 NODE_GPU,
26 NODE_BUS,
27 NODE_DDR,
28 NODE_MAX
29 };
30
31 struct test_node_data {
32 int id;
33 const char *name;
34 int num_links;
35 int links[2];
36 };
37
38 /*
39 * Static Topology:
40 * CPU -\
41 * -> BUS -> DDR
42 * GPU -/
43 */
44 static const struct test_node_data test_topology[] = {
45 { NODE_CPU, "cpu", 1, { NODE_BUS } },
46 { NODE_GPU, "gpu", 1, { NODE_BUS } },
47 { NODE_BUS, "bus", 1, { NODE_DDR } },
48 { NODE_DDR, "ddr", 0, { } },
49 };
50
51 struct icc_test_priv {
52 struct icc_provider provider;
53 struct platform_device *pdev;
54 struct icc_node *nodes[NODE_MAX];
55 };
56
get_node(struct icc_test_priv * priv,int id)57 static struct icc_node *get_node(struct icc_test_priv *priv, int id)
58 {
59 int idx = id - NODE_CPU;
60
61 if (idx < 0 || idx >= ARRAY_SIZE(test_topology))
62 return NULL;
63 return priv->nodes[idx];
64 }
65
icc_test_set(struct icc_node * src,struct icc_node * dst)66 static int icc_test_set(struct icc_node *src, struct icc_node *dst)
67 {
68 return 0;
69 }
70
icc_test_aggregate(struct icc_node * node,u32 tag,u32 avg_bw,u32 peak_bw,u32 * agg_avg,u32 * agg_peak)71 static int icc_test_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
72 u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
73 {
74 return icc_std_aggregate(node, tag, avg_bw, peak_bw, agg_avg, agg_peak);
75 }
76
icc_test_xlate(const struct of_phandle_args * spec,void * data)77 static struct icc_node *icc_test_xlate(const struct of_phandle_args *spec, void *data)
78 {
79 return NULL;
80 }
81
icc_test_get_bw(struct icc_node * node,u32 * avg,u32 * peak)82 static int icc_test_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
83 {
84 *avg = 0;
85 *peak = 0;
86
87 return 0;
88 }
89
icc_test_init(struct kunit * test)90 static int icc_test_init(struct kunit *test)
91 {
92 struct icc_test_priv *priv;
93 struct icc_node *node;
94 int i, j, ret;
95
96 priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
97 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
98 test->priv = priv;
99
100 priv->pdev = kunit_platform_device_alloc(test, "icc-test-dev", -1);
101 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->pdev);
102 KUNIT_ASSERT_EQ(test, kunit_platform_device_add(test, priv->pdev), 0);
103
104 priv->provider.set = icc_test_set;
105 priv->provider.aggregate = icc_test_aggregate;
106 priv->provider.xlate = icc_test_xlate;
107 priv->provider.get_bw = icc_test_get_bw;
108 priv->provider.dev = &priv->pdev->dev;
109 priv->provider.data = priv;
110 INIT_LIST_HEAD(&priv->provider.nodes);
111
112 ret = icc_provider_register(&priv->provider);
113 KUNIT_ASSERT_EQ(test, ret, 0);
114
115 for (i = 0; i < ARRAY_SIZE(test_topology); i++) {
116 const struct test_node_data *data = &test_topology[i];
117
118 node = icc_node_create(data->id);
119 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
120
121 node->name = data->name;
122 icc_node_add(node, &priv->provider);
123 priv->nodes[i] = node;
124 }
125
126 for (i = 0; i < ARRAY_SIZE(test_topology); i++) {
127 const struct test_node_data *data = &test_topology[i];
128 struct icc_node *src = get_node(priv, data->id);
129
130 for (j = 0; j < data->num_links; j++) {
131 ret = icc_link_create(src, data->links[j]);
132 KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Failed to link %s->%d",
133 src->name, data->links[j]);
134 }
135 }
136
137 icc_sync_state(&priv->pdev->dev);
138
139 return 0;
140 }
141
icc_test_exit(struct kunit * test)142 static void icc_test_exit(struct kunit *test)
143 {
144 struct icc_test_priv *priv = test->priv;
145
146 icc_nodes_remove(&priv->provider);
147 icc_provider_deregister(&priv->provider);
148 }
149
150 /*
151 * Helper to construct a mock path.
152 *
153 * Because we are bypassing icc_get(), we must manually link the requests
154 * to the nodes' req_list so that icc_std_aggregate() can discover them.
155 */
icc_test_create_path(struct kunit * test,struct icc_node ** nodes,int num)156 static struct icc_path *icc_test_create_path(struct kunit *test,
157 struct icc_node **nodes, int num)
158 {
159 struct icc_path *path;
160 int i;
161
162 path = kunit_kzalloc(test, struct_size(path, reqs, num), GFP_KERNEL);
163 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, path);
164
165 path->num_nodes = num;
166 for (i = 0; i < num; i++) {
167 path->reqs[i].node = nodes[i];
168 hlist_add_head(&path->reqs[i].req_node, &nodes[i]->req_list);
169 }
170 path->name = "mock-path";
171
172 return path;
173 }
174
icc_test_destroy_path(struct kunit * test,struct icc_path * path)175 static void icc_test_destroy_path(struct kunit *test, struct icc_path *path)
176 {
177 int i;
178
179 for (i = 0; i < path->num_nodes; i++)
180 hlist_del(&path->reqs[i].req_node);
181
182 kunit_kfree(test, path);
183 }
184
icc_test_topology_integrity(struct kunit * test)185 static void icc_test_topology_integrity(struct kunit *test)
186 {
187 struct icc_test_priv *priv = test->priv;
188 struct icc_node *cpu = get_node(priv, NODE_CPU);
189 struct icc_node *bus = get_node(priv, NODE_BUS);
190
191 KUNIT_EXPECT_EQ(test, cpu->num_links, 1);
192 KUNIT_EXPECT_PTR_EQ(test, cpu->links[0], bus);
193 KUNIT_EXPECT_PTR_EQ(test, cpu->provider, &priv->provider);
194 }
195
icc_test_set_bw(struct kunit * test)196 static void icc_test_set_bw(struct kunit *test)
197 {
198 struct icc_test_priv *priv = test->priv;
199 struct icc_path *path;
200 struct icc_node *path_nodes[3];
201 int ret;
202
203 /* Path: CPU -> BUS -> DDR */
204 path_nodes[0] = get_node(priv, NODE_CPU);
205 path_nodes[1] = get_node(priv, NODE_BUS);
206 path_nodes[2] = get_node(priv, NODE_DDR);
207
208 path = icc_test_create_path(test, path_nodes, 3);
209
210 ret = icc_enable(path);
211 KUNIT_ASSERT_EQ(test, ret, 0);
212
213 ret = icc_set_bw(path, 1000, 2000);
214 KUNIT_EXPECT_EQ(test, ret, 0);
215
216 KUNIT_EXPECT_EQ(test, path_nodes[0]->avg_bw, 1000);
217 KUNIT_EXPECT_EQ(test, path_nodes[0]->peak_bw, 2000);
218 KUNIT_EXPECT_EQ(test, path_nodes[1]->avg_bw, 1000);
219 KUNIT_EXPECT_EQ(test, path_nodes[1]->peak_bw, 2000);
220
221 icc_set_tag(path, 0xABC);
222 KUNIT_EXPECT_EQ(test, path->reqs[0].tag, 0xABC);
223
224 icc_disable(path);
225 KUNIT_EXPECT_EQ(test, path_nodes[0]->avg_bw, 0);
226
227 icc_test_destroy_path(test, path);
228 }
229
icc_test_aggregation(struct kunit * test)230 static void icc_test_aggregation(struct kunit *test)
231 {
232 struct icc_test_priv *priv = test->priv;
233 struct icc_path *path_cpu, *path_gpu;
234 struct icc_node *nodes_cpu[3], *nodes_gpu[2];
235 struct icc_node *bus = get_node(priv, NODE_BUS);
236 int ret;
237
238 nodes_cpu[0] = get_node(priv, NODE_CPU);
239 nodes_cpu[1] = bus;
240 nodes_cpu[2] = get_node(priv, NODE_DDR);
241 path_cpu = icc_test_create_path(test, nodes_cpu, 3);
242
243 nodes_gpu[0] = get_node(priv, NODE_GPU);
244 nodes_gpu[1] = bus;
245 path_gpu = icc_test_create_path(test, nodes_gpu, 2);
246
247 icc_enable(path_cpu);
248 icc_enable(path_gpu);
249
250 ret = icc_set_bw(path_cpu, 1000, 1000);
251 KUNIT_EXPECT_EQ(test, ret, 0);
252 KUNIT_EXPECT_EQ(test, bus->avg_bw, 1000);
253
254 ret = icc_set_bw(path_gpu, 2000, 2000);
255 KUNIT_EXPECT_EQ(test, ret, 0);
256
257 /* Bus aggregates: CPU(1000) + GPU(2000) */
258 KUNIT_EXPECT_EQ(test, bus->avg_bw, 3000);
259 /* Peak aggregates: max(CPU, GPU) */
260 KUNIT_EXPECT_EQ(test, bus->peak_bw, 2000);
261
262 icc_test_destroy_path(test, path_cpu);
263 icc_test_destroy_path(test, path_gpu);
264 }
265
icc_test_bulk_ops(struct kunit * test)266 static void icc_test_bulk_ops(struct kunit *test)
267 {
268 struct icc_test_priv *priv = test->priv;
269 struct icc_node *nodes_cpu[3], *nodes_gpu[2];
270 struct icc_bulk_data bulk[2];
271 int ret;
272
273 nodes_cpu[0] = get_node(priv, NODE_CPU);
274 nodes_cpu[1] = get_node(priv, NODE_BUS);
275 nodes_cpu[2] = get_node(priv, NODE_DDR);
276
277 nodes_gpu[0] = get_node(priv, NODE_GPU);
278 nodes_gpu[1] = get_node(priv, NODE_BUS);
279
280 bulk[0].path = icc_test_create_path(test, nodes_cpu, 3);
281 bulk[0].avg_bw = 500;
282 bulk[0].peak_bw = 500;
283
284 bulk[1].path = icc_test_create_path(test, nodes_gpu, 2);
285 bulk[1].avg_bw = 600;
286 bulk[1].peak_bw = 600;
287
288 ret = icc_bulk_set_bw(2, bulk);
289 KUNIT_EXPECT_EQ(test, ret, 0);
290 /* Paths disabled, bandwidth should be 0 */
291 KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 0);
292
293 ret = icc_bulk_enable(2, bulk);
294 KUNIT_EXPECT_EQ(test, ret, 0);
295 /* Paths enabled, aggregation applies */
296 KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 1100);
297
298 icc_bulk_disable(2, bulk);
299 KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 0);
300
301 icc_test_destroy_path(test, bulk[0].path);
302 icc_test_destroy_path(test, bulk[1].path);
303 }
304
305 static struct kunit_case icc_test_cases[] = {
306 KUNIT_CASE(icc_test_topology_integrity),
307 KUNIT_CASE(icc_test_set_bw),
308 KUNIT_CASE(icc_test_aggregation),
309 KUNIT_CASE(icc_test_bulk_ops),
310 {}
311 };
312
313 static struct kunit_suite icc_test_suite = {
314 .name = "interconnect",
315 .init = icc_test_init,
316 .exit = icc_test_exit,
317 .test_cases = icc_test_cases,
318 };
319
320 kunit_test_suite(icc_test_suite);
321
322 MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
323 MODULE_DESCRIPTION("KUnit tests for the Interconnect framework");
324 MODULE_LICENSE("GPL");
325