Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause 2 : : * Copyright(c) 2022 Ericsson AB 3 : : */ 4 : : 5 : : #include <rte_seqlock.h> 6 : : 7 : : #include <rte_cycles.h> 8 : : #include <rte_malloc.h> 9 : : #include <rte_random.h> 10 : : 11 : : #include <inttypes.h> 12 : : 13 : : #include "test.h" 14 : : 15 : : struct __rte_cache_aligned data { 16 : : rte_seqlock_t lock; 17 : : 18 : : uint64_t a; 19 : : alignas(RTE_CACHE_LINE_SIZE) uint64_t b; 20 : : alignas(RTE_CACHE_LINE_SIZE) uint64_t c; 21 : : }; 22 : : 23 : : struct reader { 24 : : struct data *data; 25 : : RTE_ATOMIC(uint8_t) stop; 26 : : }; 27 : : 28 : : #define WRITER_RUNTIME 2.0 /* s */ 29 : : 30 : : #define WRITER_MAX_DELAY 100 /* us */ 31 : : 32 : : #define INTERRUPTED_WRITER_FREQUENCY 1000 33 : : #define WRITER_INTERRUPT_TIME 1 /* us */ 34 : : 35 : : static int 36 : 0 : writer_run(void *arg) 37 : : { 38 : : struct data *data = arg; 39 : : uint64_t deadline; 40 : : 41 : 0 : deadline = rte_get_timer_cycles() + 42 : 0 : WRITER_RUNTIME * rte_get_timer_hz(); 43 : : 44 [ # # ]: 0 : while (rte_get_timer_cycles() < deadline) { 45 : : bool interrupted; 46 : : uint64_t new_value; 47 : : unsigned int delay; 48 : : 49 : 0 : new_value = rte_rand(); 50 : : 51 : 0 : interrupted = rte_rand_max(INTERRUPTED_WRITER_FREQUENCY) == 0; 52 : : 53 : 0 : rte_seqlock_write_lock(&data->lock); 54 : : 55 : 0 : data->c = new_value; 56 : 0 : data->b = new_value; 57 : : 58 [ # # ]: 0 : if (interrupted) 59 : 0 : rte_delay_us_block(WRITER_INTERRUPT_TIME); 60 : : 61 : 0 : data->a = new_value; 62 : : 63 : : rte_seqlock_write_unlock(&data->lock); 64 : : 65 : 0 : delay = rte_rand_max(WRITER_MAX_DELAY); 66 : : 67 : 0 : rte_delay_us_block(delay); 68 : : } 69 : : 70 : 0 : return TEST_SUCCESS; 71 : : } 72 : : 73 : : #define INTERRUPTED_READER_FREQUENCY 1000 74 : : #define READER_INTERRUPT_TIME 1000 /* us */ 75 : : 76 : : static int 77 : 0 : reader_run(void *arg) 78 : : { 79 : : struct reader *r = arg; 80 : : int rc = TEST_SUCCESS; 81 : : 82 [ # # # # ]: 0 : while (rte_atomic_load_explicit(&r->stop, rte_memory_order_relaxed) == 0 && 83 : : rc == TEST_SUCCESS) { 84 : 0 : struct data *data = r->data; 85 : : bool interrupted; 86 : : uint32_t sn; 87 : : uint64_t a; 88 : : uint64_t b; 89 : : uint64_t c; 90 : : 91 : 0 : interrupted = rte_rand_max(INTERRUPTED_READER_FREQUENCY) == 0; 92 : : 93 : : do { 94 : : sn = rte_seqlock_read_begin(&data->lock); 95 : : 96 : 0 : a = data->a; 97 [ # # ]: 0 : if (interrupted) 98 : 0 : rte_delay_us_block(READER_INTERRUPT_TIME); 99 : 0 : c = data->c; 100 : 0 : b = data->b; 101 : : 102 [ # # ]: 0 : } while (rte_seqlock_read_retry(&data->lock, sn)); 103 : : 104 [ # # ]: 0 : if (a != b || b != c) { 105 : : printf("Reader observed inconsistent data values " 106 : : "%" PRIu64 " %" PRIu64 " %" PRIu64 "\n", 107 : : a, b, c); 108 : : rc = TEST_FAILED; 109 : : } 110 : : } 111 : : 112 : 0 : return rc; 113 : : } 114 : : 115 : : static void 116 : : reader_stop(struct reader *reader) 117 : : { 118 : 0 : rte_atomic_store_explicit(&reader->stop, 1, rte_memory_order_relaxed); 119 : : } 120 : : 121 : : #define NUM_WRITERS 2 /* main lcore + one worker */ 122 : : #define MIN_NUM_READERS 2 123 : : #define MIN_LCORE_COUNT (NUM_WRITERS + MIN_NUM_READERS) 124 : : 125 : : /* Only a compile-time test */ 126 : : static rte_seqlock_t __rte_unused static_init_lock = RTE_SEQLOCK_INITIALIZER; 127 : : 128 : : static int 129 : 1 : test_seqlock(void) 130 : : { 131 : : struct reader readers[RTE_MAX_LCORE]; 132 : : unsigned int num_lcores; 133 : : unsigned int num_readers; 134 : : struct data *data; 135 : : unsigned int i; 136 : : unsigned int lcore_id; 137 : : unsigned int reader_lcore_ids[RTE_MAX_LCORE]; 138 : : unsigned int worker_writer_lcore_id = 0; 139 : : int rc = TEST_SUCCESS; 140 : : 141 : 1 : num_lcores = rte_lcore_count(); 142 : : 143 [ + - ]: 1 : if (num_lcores < MIN_LCORE_COUNT) { 144 : : printf("Too few cores to run test. Skipping.\n"); 145 : 1 : return TEST_SKIPPED; 146 : : } 147 : : 148 : 0 : num_readers = num_lcores - NUM_WRITERS; 149 : : 150 : 0 : data = rte_zmalloc(NULL, sizeof(struct data), 0); 151 : : 152 [ # # ]: 0 : if (data == NULL) { 153 : : printf("Failed to allocate memory for seqlock data\n"); 154 : 0 : return TEST_FAILED; 155 : : } 156 : : 157 : : i = 0; 158 [ # # ]: 0 : RTE_LCORE_FOREACH_WORKER(lcore_id) { 159 [ # # ]: 0 : if (i == 0) { 160 : 0 : rte_eal_remote_launch(writer_run, data, lcore_id); 161 : : worker_writer_lcore_id = lcore_id; 162 : : } else { 163 : 0 : unsigned int reader_idx = i - 1; 164 : 0 : struct reader *reader = &readers[reader_idx]; 165 : : 166 : 0 : reader->data = data; 167 : 0 : reader->stop = 0; 168 : : 169 : 0 : rte_eal_remote_launch(reader_run, reader, lcore_id); 170 : 0 : reader_lcore_ids[reader_idx] = lcore_id; 171 : : } 172 : 0 : i++; 173 : : } 174 : : 175 [ # # # # ]: 0 : if (writer_run(data) != 0 || 176 : 0 : rte_eal_wait_lcore(worker_writer_lcore_id) != 0) 177 : : rc = TEST_FAILED; 178 : : 179 [ # # ]: 0 : for (i = 0; i < num_readers; i++) { 180 : : reader_stop(&readers[i]); 181 [ # # ]: 0 : if (rte_eal_wait_lcore(reader_lcore_ids[i]) != 0) 182 : : rc = TEST_FAILED; 183 : : } 184 : : 185 : 0 : rte_free(data); 186 : : 187 : 0 : return rc; 188 : : } 189 : : 190 : 251 : REGISTER_FAST_TEST(seqlock_autotest, true, true, test_seqlock);