Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

interconnect: Add kunit tests for core functionality

The interconnect framework currently lacks in-tree unit tests to verify
the core logic in isolation. This makes it difficult to validate
regression stability when modifying the provider/consumer APIs or
aggregation logic.

Introduce a kunit test suite that verifies the fundamental behavior of
the subsystem. The tests cover:
- Provider API (node creation, linking, topology construction).
- Consumer API (path enabling/disabling, bandwidth requests).
- Standard aggregation logic (accumulating bandwidth across links).
- Bulk operations for setting bandwidth on multiple paths.

The suite simulates a simple SoC topology with multiple masters and a
shared bus to validate traffic aggregation behavior in a controlled
software environment, without requiring specific hardware or Device
Tree support.

Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Link: https://lore.kernel.org/r/20260110184309.906735-1-visitorckw@gmail.com
Signed-off-by: Georgi Djakov <djakov@kernel.org>

authored by

Kuan-Wei Chiu and committed by
Georgi Djakov
06ebbe71 a3e2ea79

+340
+14
drivers/interconnect/Kconfig
··· 22 22 help 23 23 Support for wrapping clocks into the interconnect nodes. 24 24 25 + config INTERCONNECT_KUNIT_TEST 26 + tristate "KUnit tests for Interconnect framework" 27 + depends on KUNIT 28 + default KUNIT_ALL_TESTS 29 + help 30 + This builds the KUnit test suite for the generic system interconnect 31 + framework. 32 + 33 + The tests cover the core functionality of the interconnect subsystem, 34 + including provider/consumer APIs, topology management, and bandwidth 35 + aggregation logic. 36 + 37 + If unsure, say N. 38 + 25 39 endif
+2
drivers/interconnect/Makefile
··· 10 10 obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/ 11 11 12 12 obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o 13 + 14 + obj-$(CONFIG_INTERCONNECT_KUNIT_TEST) += icc-kunit.o
+324
drivers/interconnect/icc-kunit.c
··· 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");