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