|
3 | 3 | * Copyright(c) 2024 Ericsson AB |
4 | 4 | */ |
5 | 5 |
|
| 6 | +#include <inttypes.h> |
6 | 7 | #include <stdbool.h> |
7 | 8 |
|
8 | | -#include <rte_launch.h> |
9 | 9 | #include <rte_bitops.h> |
| 10 | +#include <rte_cycles.h> |
| 11 | +#include <rte_launch.h> |
| 12 | +#include <rte_lcore.h> |
10 | 13 | #include <rte_random.h> |
11 | 14 | #include "test.h" |
12 | 15 |
|
@@ -53,6 +56,221 @@ GEN_TEST_BIT_ACCESS(test_bit_access32, rte_bit_set, rte_bit_clear, rte_bit_assig |
53 | 56 | GEN_TEST_BIT_ACCESS(test_bit_access64, rte_bit_set, rte_bit_clear, rte_bit_assign, rte_bit_flip, |
54 | 57 | rte_bit_test, 64) |
55 | 58 |
|
| 59 | +#define bit_atomic_set(addr, nr) \ |
| 60 | + rte_bit_atomic_set(addr, nr, rte_memory_order_relaxed) |
| 61 | + |
| 62 | +#define bit_atomic_clear(addr, nr) \ |
| 63 | + rte_bit_atomic_clear(addr, nr, rte_memory_order_relaxed) |
| 64 | + |
| 65 | +#define bit_atomic_assign(addr, nr, value) \ |
| 66 | + rte_bit_atomic_assign(addr, nr, value, rte_memory_order_relaxed) |
| 67 | + |
| 68 | +#define bit_atomic_flip(addr, nr) \ |
| 69 | + rte_bit_atomic_flip(addr, nr, rte_memory_order_relaxed) |
| 70 | + |
| 71 | +#define bit_atomic_test(addr, nr) \ |
| 72 | + rte_bit_atomic_test(addr, nr, rte_memory_order_relaxed) |
| 73 | + |
| 74 | +GEN_TEST_BIT_ACCESS(test_bit_atomic_access32, bit_atomic_set, bit_atomic_clear, bit_atomic_assign, |
| 75 | + bit_atomic_flip, bit_atomic_test, 32) |
| 76 | + |
| 77 | +GEN_TEST_BIT_ACCESS(test_bit_atomic_access64, bit_atomic_set, bit_atomic_clear, bit_atomic_assign, |
| 78 | + bit_atomic_flip, bit_atomic_test, 64) |
| 79 | + |
| 80 | +#define PARALLEL_TEST_RUNTIME 0.25 |
| 81 | + |
| 82 | +#define GEN_TEST_BIT_PARALLEL_ASSIGN(size) \ |
| 83 | +struct parallel_access_lcore ## size \ |
| 84 | +{ \ |
| 85 | + unsigned int bit; \ |
| 86 | + uint ## size ##_t *word; \ |
| 87 | + bool failed; \ |
| 88 | +}; \ |
| 89 | +static int \ |
| 90 | +run_parallel_assign ## size(void *arg) \ |
| 91 | +{ \ |
| 92 | + struct parallel_access_lcore ## size *lcore = arg; \ |
| 93 | + uint64_t deadline = rte_get_timer_cycles() + PARALLEL_TEST_RUNTIME * rte_get_timer_hz(); \ |
| 94 | + bool value = false; \ |
| 95 | + do { \ |
| 96 | + bool new_value = rte_rand() & 1; \ |
| 97 | + bool use_test_and_modify = rte_rand() & 1; \ |
| 98 | + bool use_assign = rte_rand() & 1; \ |
| 99 | + if (rte_bit_atomic_test(lcore->word, lcore->bit, \ |
| 100 | + rte_memory_order_relaxed) != value) { \ |
| 101 | + lcore->failed = true; \ |
| 102 | + break; \ |
| 103 | + } \ |
| 104 | + if (use_test_and_modify) { \ |
| 105 | + bool old_value; \ |
| 106 | + if (use_assign) { \ |
| 107 | + old_value = rte_bit_atomic_test_and_assign(lcore->word, \ |
| 108 | + lcore->bit, new_value, rte_memory_order_relaxed); \ |
| 109 | + } else { \ |
| 110 | + old_value = new_value ? \ |
| 111 | + rte_bit_atomic_test_and_set(lcore->word, lcore->bit, \ |
| 112 | + rte_memory_order_relaxed) : \ |
| 113 | + rte_bit_atomic_test_and_clear(lcore->word, lcore->bit, \ |
| 114 | + rte_memory_order_relaxed); \ |
| 115 | + } \ |
| 116 | + if (old_value != value) { \ |
| 117 | + lcore->failed = true; \ |
| 118 | + break; \ |
| 119 | + } \ |
| 120 | + } else { \ |
| 121 | + if (use_assign) { \ |
| 122 | + rte_bit_atomic_assign(lcore->word, lcore->bit, new_value, \ |
| 123 | + rte_memory_order_relaxed); \ |
| 124 | + } else { \ |
| 125 | + if (new_value) \ |
| 126 | + rte_bit_atomic_set(lcore->word, lcore->bit, \ |
| 127 | + rte_memory_order_relaxed); \ |
| 128 | + else \ |
| 129 | + rte_bit_atomic_clear(lcore->word, lcore->bit, \ |
| 130 | + rte_memory_order_relaxed); \ |
| 131 | + } \ |
| 132 | + } \ |
| 133 | + value = new_value; \ |
| 134 | + } while (rte_get_timer_cycles() < deadline); \ |
| 135 | + return 0; \ |
| 136 | +} \ |
| 137 | +static int \ |
| 138 | +test_bit_atomic_parallel_assign ## size(void) \ |
| 139 | +{ \ |
| 140 | + unsigned int worker_lcore_id; \ |
| 141 | + uint ## size ## _t word = 0; \ |
| 142 | + struct parallel_access_lcore ## size lmain = { .word = &word }; \ |
| 143 | + struct parallel_access_lcore ## size lworker = { .word = &word }; \ |
| 144 | + if (rte_lcore_count() < 2) { \ |
| 145 | + printf("Need multiple cores to run parallel test.\n"); \ |
| 146 | + return TEST_SKIPPED; \ |
| 147 | + } \ |
| 148 | + worker_lcore_id = rte_get_next_lcore(-1, 1, 0); \ |
| 149 | + lmain.bit = rte_rand_max(size); \ |
| 150 | + do { \ |
| 151 | + lworker.bit = rte_rand_max(size); \ |
| 152 | + } while (lworker.bit == lmain.bit); \ |
| 153 | + int rc = rte_eal_remote_launch(run_parallel_assign ## size, &lworker, worker_lcore_id); \ |
| 154 | + TEST_ASSERT(rc == 0, "Worker thread launch failed"); \ |
| 155 | + run_parallel_assign ## size(&lmain); \ |
| 156 | + rte_eal_mp_wait_lcore(); \ |
| 157 | + TEST_ASSERT(!lmain.failed, "Main lcore atomic access failed"); \ |
| 158 | + TEST_ASSERT(!lworker.failed, "Worker lcore atomic access failed"); \ |
| 159 | + return TEST_SUCCESS; \ |
| 160 | +} |
| 161 | + |
| 162 | +GEN_TEST_BIT_PARALLEL_ASSIGN(32) |
| 163 | +GEN_TEST_BIT_PARALLEL_ASSIGN(64) |
| 164 | + |
| 165 | +#define GEN_TEST_BIT_PARALLEL_TEST_AND_MODIFY(size) \ |
| 166 | +struct parallel_test_and_set_lcore ## size \ |
| 167 | +{ \ |
| 168 | + uint ## size ##_t *word; \ |
| 169 | + unsigned int bit; \ |
| 170 | + uint64_t flips; \ |
| 171 | +}; \ |
| 172 | +static int \ |
| 173 | +run_parallel_test_and_modify ## size(void *arg) \ |
| 174 | +{ \ |
| 175 | + struct parallel_test_and_set_lcore ## size *lcore = arg; \ |
| 176 | + uint64_t deadline = rte_get_timer_cycles() + PARALLEL_TEST_RUNTIME * rte_get_timer_hz(); \ |
| 177 | + do { \ |
| 178 | + bool old_value; \ |
| 179 | + bool new_value = rte_rand() & 1; \ |
| 180 | + bool use_assign = rte_rand() & 1; \ |
| 181 | + if (use_assign) \ |
| 182 | + old_value = rte_bit_atomic_test_and_assign(lcore->word, lcore->bit, \ |
| 183 | + new_value, rte_memory_order_relaxed); \ |
| 184 | + else \ |
| 185 | + old_value = new_value ? \ |
| 186 | + rte_bit_atomic_test_and_set(lcore->word, lcore->bit, \ |
| 187 | + rte_memory_order_relaxed) : \ |
| 188 | + rte_bit_atomic_test_and_clear(lcore->word, lcore->bit, \ |
| 189 | + rte_memory_order_relaxed); \ |
| 190 | + if (old_value != new_value) \ |
| 191 | + lcore->flips++; \ |
| 192 | + } while (rte_get_timer_cycles() < deadline); \ |
| 193 | + return 0; \ |
| 194 | +} \ |
| 195 | +static int \ |
| 196 | +test_bit_atomic_parallel_test_and_modify ## size(void) \ |
| 197 | +{ \ |
| 198 | + unsigned int worker_lcore_id; \ |
| 199 | + uint ## size ## _t word = 0; \ |
| 200 | + unsigned int bit = rte_rand_max(size); \ |
| 201 | + struct parallel_test_and_set_lcore ## size lmain = { .word = &word, .bit = bit }; \ |
| 202 | + struct parallel_test_and_set_lcore ## size lworker = { .word = &word, .bit = bit }; \ |
| 203 | + if (rte_lcore_count() < 2) { \ |
| 204 | + printf("Need multiple cores to run parallel test.\n"); \ |
| 205 | + return TEST_SKIPPED; \ |
| 206 | + } \ |
| 207 | + worker_lcore_id = rte_get_next_lcore(-1, 1, 0); \ |
| 208 | + int rc = rte_eal_remote_launch(run_parallel_test_and_modify ## size, &lworker, \ |
| 209 | + worker_lcore_id); \ |
| 210 | + TEST_ASSERT(rc == 0, "Worker thread launch failed"); \ |
| 211 | + run_parallel_test_and_modify ## size(&lmain); \ |
| 212 | + rte_eal_mp_wait_lcore(); \ |
| 213 | + uint64_t total_flips = lmain.flips + lworker.flips; \ |
| 214 | + bool expected_value = total_flips % 2; \ |
| 215 | + TEST_ASSERT(expected_value == rte_bit_test(&word, bit), \ |
| 216 | + "After %"PRId64" flips, the bit value should be %d", total_flips, expected_value); \ |
| 217 | + uint64_t expected_word = 0; \ |
| 218 | + rte_bit_assign(&expected_word, bit, expected_value); \ |
| 219 | + TEST_ASSERT(expected_word == word, "Untouched bits have changed value"); \ |
| 220 | + return TEST_SUCCESS; \ |
| 221 | +} |
| 222 | + |
| 223 | +GEN_TEST_BIT_PARALLEL_TEST_AND_MODIFY(32) |
| 224 | +GEN_TEST_BIT_PARALLEL_TEST_AND_MODIFY(64) |
| 225 | + |
| 226 | +#define GEN_TEST_BIT_PARALLEL_FLIP(size) \ |
| 227 | +struct parallel_flip_lcore ## size \ |
| 228 | +{ \ |
| 229 | + uint ## size ##_t *word; \ |
| 230 | + unsigned int bit; \ |
| 231 | + uint64_t flips; \ |
| 232 | +}; \ |
| 233 | +static int \ |
| 234 | +run_parallel_flip ## size(void *arg) \ |
| 235 | +{ \ |
| 236 | + struct parallel_flip_lcore ## size *lcore = arg; \ |
| 237 | + uint64_t deadline = rte_get_timer_cycles() + PARALLEL_TEST_RUNTIME * rte_get_timer_hz(); \ |
| 238 | + do { \ |
| 239 | + rte_bit_atomic_flip(lcore->word, lcore->bit, rte_memory_order_relaxed); \ |
| 240 | + lcore->flips++; \ |
| 241 | + } while (rte_get_timer_cycles() < deadline); \ |
| 242 | + return 0; \ |
| 243 | +} \ |
| 244 | +static int \ |
| 245 | +test_bit_atomic_parallel_flip ## size(void) \ |
| 246 | +{ \ |
| 247 | + unsigned int worker_lcore_id; \ |
| 248 | + uint ## size ## _t word = 0; \ |
| 249 | + unsigned int bit = rte_rand_max(size); \ |
| 250 | + struct parallel_flip_lcore ## size lmain = { .word = &word, .bit = bit }; \ |
| 251 | + struct parallel_flip_lcore ## size lworker = { .word = &word, .bit = bit }; \ |
| 252 | + if (rte_lcore_count() < 2) { \ |
| 253 | + printf("Need multiple cores to run parallel test.\n"); \ |
| 254 | + return TEST_SKIPPED; \ |
| 255 | + } \ |
| 256 | + worker_lcore_id = rte_get_next_lcore(-1, 1, 0); \ |
| 257 | + int rc = rte_eal_remote_launch(run_parallel_flip ## size, &lworker, worker_lcore_id); \ |
| 258 | + TEST_ASSERT(rc == 0, "Worker thread launch failed"); \ |
| 259 | + run_parallel_flip ## size(&lmain); \ |
| 260 | + rte_eal_mp_wait_lcore(); \ |
| 261 | + uint64_t total_flips = lmain.flips + lworker.flips; \ |
| 262 | + bool expected_value = total_flips % 2; \ |
| 263 | + TEST_ASSERT(expected_value == rte_bit_test(&word, bit), \ |
| 264 | + "After %"PRId64" flips, the bit value should be %d", total_flips, expected_value); \ |
| 265 | + uint64_t expected_word = 0; \ |
| 266 | + rte_bit_assign(&expected_word, bit, expected_value); \ |
| 267 | + TEST_ASSERT(expected_word == word, "Untouched bits have changed value"); \ |
| 268 | + return TEST_SUCCESS; \ |
| 269 | +} |
| 270 | + |
| 271 | +GEN_TEST_BIT_PARALLEL_FLIP(32) |
| 272 | +GEN_TEST_BIT_PARALLEL_FLIP(64) |
| 273 | + |
56 | 274 | static uint32_t val32; |
57 | 275 | static uint64_t val64; |
58 | 276 |
|
@@ -169,6 +387,16 @@ static struct unit_test_suite test_suite = { |
169 | 387 | .unit_test_cases = { |
170 | 388 | TEST_CASE(test_bit_access32), |
171 | 389 | TEST_CASE(test_bit_access64), |
| 390 | + TEST_CASE(test_bit_access32), |
| 391 | + TEST_CASE(test_bit_access64), |
| 392 | + TEST_CASE(test_bit_atomic_access32), |
| 393 | + TEST_CASE(test_bit_atomic_access64), |
| 394 | + TEST_CASE(test_bit_atomic_parallel_assign32), |
| 395 | + TEST_CASE(test_bit_atomic_parallel_assign64), |
| 396 | + TEST_CASE(test_bit_atomic_parallel_test_and_modify32), |
| 397 | + TEST_CASE(test_bit_atomic_parallel_test_and_modify64), |
| 398 | + TEST_CASE(test_bit_atomic_parallel_flip32), |
| 399 | + TEST_CASE(test_bit_atomic_parallel_flip64), |
172 | 400 | TEST_CASE(test_bit_relaxed_set), |
173 | 401 | TEST_CASE(test_bit_relaxed_clear), |
174 | 402 | TEST_CASE(test_bit_relaxed_test_set_clear), |
|
0 commit comments