1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2024 Ericsson AB 3 */ 4 5 #include <inttypes.h> 6 #include <stdio.h> 7 #include <string.h> 8 9 #include <rte_launch.h> 10 #include <rte_lcore_var.h> 11 #include <rte_random.h> 12 13 #include "test.h" 14 15 #define MIN_LCORES 2 16 17 RTE_LCORE_VAR_HANDLE(int, test_int); 18 RTE_LCORE_VAR_HANDLE(char, test_char); 19 RTE_LCORE_VAR_HANDLE(long, test_long_sized); 20 RTE_LCORE_VAR_HANDLE(short, test_short); 21 RTE_LCORE_VAR_HANDLE(long, test_long_sized_aligned); 22 23 struct int_checker_state { 24 int old_value; 25 int new_value; 26 bool success; 27 }; 28 29 static void 30 rand_blk(void *blk, size_t size) 31 { 32 size_t i; 33 34 for (i = 0; i < size; i++) 35 ((unsigned char *)blk)[i] = (unsigned char)rte_rand(); 36 } 37 38 static bool 39 is_ptr_aligned(const void *ptr, size_t align) 40 { 41 return ptr != NULL ? (uintptr_t)ptr % align == 0 : false; 42 } 43 44 static int 45 check_int(void *arg) 46 { 47 struct int_checker_state *state = arg; 48 49 int *ptr = RTE_LCORE_VAR(test_int); 50 51 bool naturally_aligned = is_ptr_aligned(ptr, sizeof(int)); 52 53 bool equal = *(RTE_LCORE_VAR(test_int)) == state->old_value; 54 55 state->success = equal && naturally_aligned; 56 57 *ptr = state->new_value; 58 59 return 0; 60 } 61 62 RTE_LCORE_VAR_INIT(test_int); 63 RTE_LCORE_VAR_INIT(test_char); 64 RTE_LCORE_VAR_INIT_SIZE(test_long_sized, 32); 65 RTE_LCORE_VAR_INIT(test_short); 66 RTE_LCORE_VAR_INIT_SIZE_ALIGN(test_long_sized_aligned, sizeof(long), RTE_CACHE_LINE_SIZE); 67 68 static int 69 test_int_lvar(void) 70 { 71 unsigned int lcore_id; 72 73 struct int_checker_state states[RTE_MAX_LCORE] = {}; 74 75 RTE_LCORE_FOREACH_WORKER(lcore_id) { 76 struct int_checker_state *state = &states[lcore_id]; 77 78 state->old_value = (int)rte_rand(); 79 state->new_value = (int)rte_rand(); 80 81 *RTE_LCORE_VAR_LCORE(lcore_id, test_int) = state->old_value; 82 } 83 84 RTE_LCORE_FOREACH_WORKER(lcore_id) 85 rte_eal_remote_launch(check_int, &states[lcore_id], lcore_id); 86 87 rte_eal_mp_wait_lcore(); 88 89 RTE_LCORE_FOREACH_WORKER(lcore_id) { 90 struct int_checker_state *state = &states[lcore_id]; 91 int value; 92 93 TEST_ASSERT(state->success, 94 "Unexpected value encountered on lcore %d", lcore_id); 95 96 value = *RTE_LCORE_VAR_LCORE(lcore_id, test_int); 97 TEST_ASSERT_EQUAL(state->new_value, value, 98 "Lcore %d failed to update int", lcore_id); 99 } 100 101 /* take the opportunity to test the foreach macro */ 102 int *v; 103 unsigned int i = 0; 104 RTE_LCORE_VAR_FOREACH(lcore_id, v, test_int) { 105 TEST_ASSERT_EQUAL(i, lcore_id, 106 "Encountered lcore id %d while expecting %d during iteration", 107 lcore_id, i); 108 TEST_ASSERT_EQUAL(states[lcore_id].new_value, *v, 109 "Unexpected value on lcore %d during iteration", lcore_id); 110 i++; 111 } 112 113 return TEST_SUCCESS; 114 } 115 116 static int 117 test_sized_alignment(void) 118 { 119 unsigned int lcore_id; 120 long *v; 121 122 RTE_LCORE_VAR_FOREACH(lcore_id, v, test_long_sized) { 123 TEST_ASSERT(is_ptr_aligned(v, alignof(long)), "Type-derived alignment failed"); 124 } 125 126 RTE_LCORE_VAR_FOREACH(lcore_id, v, test_long_sized_aligned) { 127 TEST_ASSERT(is_ptr_aligned(v, RTE_CACHE_LINE_SIZE), "Explicit alignment failed"); 128 } 129 130 return TEST_SUCCESS; 131 } 132 133 /* private, larger, struct */ 134 #define TEST_STRUCT_DATA_SIZE 1234 135 136 struct test_struct { 137 uint8_t data[TEST_STRUCT_DATA_SIZE]; 138 }; 139 140 static RTE_LCORE_VAR_HANDLE(char, before_struct); 141 static RTE_LCORE_VAR_HANDLE(struct test_struct, test_struct); 142 static RTE_LCORE_VAR_HANDLE(char, after_struct); 143 144 struct struct_checker_state { 145 struct test_struct old_value; 146 struct test_struct new_value; 147 bool success; 148 }; 149 150 static int check_struct(void *arg) 151 { 152 struct struct_checker_state *state = arg; 153 154 struct test_struct *lcore_struct = RTE_LCORE_VAR(test_struct); 155 156 bool properly_aligned = is_ptr_aligned(test_struct, alignof(struct test_struct)); 157 158 bool equal = memcmp(lcore_struct->data, state->old_value.data, TEST_STRUCT_DATA_SIZE) == 0; 159 160 state->success = equal && properly_aligned; 161 162 memcpy(lcore_struct->data, state->new_value.data, TEST_STRUCT_DATA_SIZE); 163 164 return 0; 165 } 166 167 static int 168 test_struct_lvar(void) 169 { 170 unsigned int lcore_id; 171 172 RTE_LCORE_VAR_ALLOC(before_struct); 173 RTE_LCORE_VAR_ALLOC(test_struct); 174 RTE_LCORE_VAR_ALLOC(after_struct); 175 176 struct struct_checker_state states[RTE_MAX_LCORE]; 177 178 RTE_LCORE_FOREACH_WORKER(lcore_id) { 179 struct struct_checker_state *state = &states[lcore_id]; 180 181 rand_blk(state->old_value.data, TEST_STRUCT_DATA_SIZE); 182 rand_blk(state->new_value.data, TEST_STRUCT_DATA_SIZE); 183 184 memcpy(RTE_LCORE_VAR_LCORE(lcore_id, test_struct)->data, 185 state->old_value.data, TEST_STRUCT_DATA_SIZE); 186 } 187 188 RTE_LCORE_FOREACH_WORKER(lcore_id) 189 rte_eal_remote_launch(check_struct, &states[lcore_id], lcore_id); 190 191 rte_eal_mp_wait_lcore(); 192 193 RTE_LCORE_FOREACH_WORKER(lcore_id) { 194 struct struct_checker_state *state = &states[lcore_id]; 195 struct test_struct *lstruct = RTE_LCORE_VAR_LCORE(lcore_id, test_struct); 196 197 TEST_ASSERT(state->success, "Unexpected value encountered on lcore %d", lcore_id); 198 199 bool equal = memcmp(lstruct->data, state->new_value.data, 200 TEST_STRUCT_DATA_SIZE) == 0; 201 202 TEST_ASSERT(equal, "Lcore %d failed to update struct", lcore_id); 203 } 204 205 RTE_LCORE_FOREACH_WORKER(lcore_id) { 206 char before = *RTE_LCORE_VAR_LCORE(lcore_id, before_struct); 207 char after = *RTE_LCORE_VAR_LCORE(lcore_id, after_struct); 208 209 TEST_ASSERT_EQUAL(before, 0, 210 "Lcore variable before test struct was modified on lcore %d", 211 lcore_id); 212 TEST_ASSERT_EQUAL(after, 0, 213 "Lcore variable after test struct was modified on lcore %d", 214 lcore_id); 215 } 216 217 return TEST_SUCCESS; 218 } 219 220 #define TEST_ARRAY_SIZE 99 221 222 typedef uint16_t test_array_t[TEST_ARRAY_SIZE]; 223 224 static void test_array_init_rand(test_array_t a) 225 { 226 size_t i; 227 for (i = 0; i < TEST_ARRAY_SIZE; i++) 228 a[i] = (uint16_t)rte_rand(); 229 } 230 231 static bool test_array_equal(test_array_t a, test_array_t b) 232 { 233 size_t i; 234 for (i = 0; i < TEST_ARRAY_SIZE; i++) { 235 if (a[i] != b[i]) 236 return false; 237 } 238 return true; 239 } 240 241 static void test_array_copy(test_array_t dst, const test_array_t src) 242 { 243 size_t i; 244 for (i = 0; i < TEST_ARRAY_SIZE; i++) 245 dst[i] = src[i]; 246 } 247 248 static RTE_LCORE_VAR_HANDLE(char, before_array); 249 static RTE_LCORE_VAR_HANDLE(test_array_t, test_array); 250 static RTE_LCORE_VAR_HANDLE(char, after_array); 251 252 struct array_checker_state { 253 test_array_t old_value; 254 test_array_t new_value; 255 bool success; 256 }; 257 258 static int check_array(void *arg) 259 { 260 struct array_checker_state *state = arg; 261 262 test_array_t *lcore_array = RTE_LCORE_VAR(test_array); 263 264 bool properly_aligned = is_ptr_aligned(lcore_array, alignof(test_array_t)); 265 266 bool equal = test_array_equal(*lcore_array, state->old_value); 267 268 state->success = equal && properly_aligned; 269 270 test_array_copy(*lcore_array, state->new_value); 271 272 return 0; 273 } 274 275 static int 276 test_array_lvar(void) 277 { 278 unsigned int lcore_id; 279 280 RTE_LCORE_VAR_ALLOC(before_array); 281 RTE_LCORE_VAR_ALLOC(test_array); 282 RTE_LCORE_VAR_ALLOC(after_array); 283 284 struct array_checker_state states[RTE_MAX_LCORE]; 285 286 RTE_LCORE_FOREACH_WORKER(lcore_id) { 287 struct array_checker_state *state = &states[lcore_id]; 288 289 test_array_init_rand(state->new_value); 290 test_array_init_rand(state->old_value); 291 292 test_array_copy(*RTE_LCORE_VAR_LCORE(lcore_id, test_array), state->old_value); 293 } 294 295 RTE_LCORE_FOREACH_WORKER(lcore_id) 296 rte_eal_remote_launch(check_array, &states[lcore_id], lcore_id); 297 298 rte_eal_mp_wait_lcore(); 299 300 RTE_LCORE_FOREACH_WORKER(lcore_id) { 301 struct array_checker_state *state = &states[lcore_id]; 302 test_array_t *larray = RTE_LCORE_VAR_LCORE(lcore_id, test_array); 303 304 TEST_ASSERT(state->success, "Unexpected value encountered on lcore %d", lcore_id); 305 306 bool equal = test_array_equal(*larray, state->new_value); 307 308 TEST_ASSERT(equal, "Lcore %d failed to update array", lcore_id); 309 } 310 311 RTE_LCORE_FOREACH_WORKER(lcore_id) { 312 char before = *RTE_LCORE_VAR_LCORE(lcore_id, before_array); 313 char after = *RTE_LCORE_VAR_LCORE(lcore_id, after_array); 314 315 TEST_ASSERT_EQUAL(before, 0, 316 "Lcore variable before test array was modified on lcore %d", 317 lcore_id); 318 TEST_ASSERT_EQUAL(after, 0, 319 "Lcore variable after test array was modified on lcore %d", 320 lcore_id); 321 } 322 323 return TEST_SUCCESS; 324 } 325 326 #define MANY_LVARS (2 * RTE_MAX_LCORE_VAR / sizeof(uint32_t)) 327 328 static int 329 test_many_lvars(void) 330 { 331 uint32_t **handlers = malloc(sizeof(uint32_t *) * MANY_LVARS); 332 unsigned int i; 333 334 TEST_ASSERT(handlers != NULL, "Unable to allocate memory"); 335 336 for (i = 0; i < MANY_LVARS; i++) { 337 unsigned int lcore_id; 338 339 RTE_LCORE_VAR_ALLOC(handlers[i]); 340 341 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { 342 uint32_t *v = RTE_LCORE_VAR_LCORE(lcore_id, handlers[i]); 343 *v = (uint32_t)(i * lcore_id); 344 } 345 } 346 347 for (i = 0; i < MANY_LVARS; i++) { 348 unsigned int lcore_id; 349 350 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { 351 uint32_t v = *RTE_LCORE_VAR_LCORE(lcore_id, handlers[i]); 352 TEST_ASSERT_EQUAL((uint32_t)(i * lcore_id), v, 353 "Unexpected lcore variable value on lcore %d", 354 lcore_id); 355 } 356 } 357 358 free(handlers); 359 360 return TEST_SUCCESS; 361 } 362 363 static int 364 test_large_lvar(void) 365 { 366 RTE_LCORE_VAR_HANDLE(unsigned char, large); 367 unsigned int lcore_id; 368 369 RTE_LCORE_VAR_ALLOC_SIZE(large, RTE_MAX_LCORE_VAR); 370 371 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { 372 unsigned char *ptr = RTE_LCORE_VAR_LCORE(lcore_id, large); 373 374 memset(ptr, (unsigned char)lcore_id, RTE_MAX_LCORE_VAR); 375 } 376 377 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { 378 unsigned char *ptr = RTE_LCORE_VAR_LCORE(lcore_id, large); 379 size_t i; 380 381 for (i = 0; i < RTE_MAX_LCORE_VAR; i++) 382 TEST_ASSERT_EQUAL(ptr[i], (unsigned char)lcore_id, 383 "Large lcore variable value is corrupted on lcore %d.", 384 lcore_id); 385 } 386 387 return TEST_SUCCESS; 388 } 389 390 static struct unit_test_suite lcore_var_testsuite = { 391 .suite_name = "lcore variable autotest", 392 .unit_test_cases = { 393 TEST_CASE(test_int_lvar), 394 TEST_CASE(test_sized_alignment), 395 TEST_CASE(test_struct_lvar), 396 TEST_CASE(test_array_lvar), 397 TEST_CASE(test_many_lvars), 398 TEST_CASE(test_large_lvar), 399 TEST_CASES_END() 400 }, 401 }; 402 403 static int test_lcore_var(void) 404 { 405 if (rte_lcore_count() < MIN_LCORES) { 406 printf("Not enough cores for lcore_var_autotest; expecting at least %d.\n", 407 MIN_LCORES); 408 return TEST_SKIPPED; 409 } 410 411 return unit_test_suite_runner(&lcore_var_testsuite); 412 } 413 414 REGISTER_FAST_TEST(lcore_var_autotest, true, false, test_lcore_var); 415