xref: /linux/drivers/interconnect/icc-kunit.c (revision 505d195b0f96fd613a51b13dde37aa5ad301eb32)
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 
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 
66 static int icc_test_set(struct icc_node *src, struct icc_node *dst)
67 {
68 	return 0;
69 }
70 
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 
77 static struct icc_node *icc_test_xlate(const struct of_phandle_args *spec, void *data)
78 {
79 	return NULL;
80 }
81 
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 
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 
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  */
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 
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 
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 
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 
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 
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