1 /*
2 * Copyright (c) 2021 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/ztest.h>
7 #include <zephyr/ztress.h>
8 #include <zephyr/sys/ring_buffer.h>
9 #include <zephyr/sys/mutex.h>
10 #include <zephyr/random/random.h>
11 #include <stdint.h>
12
13 /**
14 * @defgroup lib_ringbuffer_tests Ringbuffer
15 * @ingroup all_tests
16 * @{
17 * @}
18 */
19
20 #define STACKSIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
21
22 #define RINGBUFFER 256
23 #define LENGTH 64
24 #define VALUE 0xb
25 #define TYPE 0xc
26
27 static ZTEST_BMEM SYS_MUTEX_DEFINE(mutex);
28 RING_BUF_ITEM_DECLARE(ringbuf, RINGBUFFER);
29 static uint32_t data_output[LENGTH];
30 static uint32_t databuffer1[LENGTH];
31 static uint32_t databuffer2[LENGTH];
32
data_write(uint32_t * input)33 static void data_write(uint32_t *input)
34 {
35 sys_mutex_lock(&mutex, K_FOREVER);
36 int ret = ring_buf_item_put(&ringbuf, TYPE, VALUE,
37 input, LENGTH);
38 zassert_equal(ret, 0);
39 sys_mutex_unlock(&mutex);
40 }
41
data_read(uint32_t * output)42 static void data_read(uint32_t *output)
43 {
44 uint16_t type;
45 uint8_t value, size32 = LENGTH;
46 int ret;
47
48 sys_mutex_lock(&mutex, K_FOREVER);
49 ret = ring_buf_item_get(&ringbuf, &type, &value, output, &size32);
50 sys_mutex_unlock(&mutex);
51
52 zassert_equal(ret, 0);
53 zassert_equal(type, TYPE);
54 zassert_equal(value, VALUE);
55 zassert_equal(size32, LENGTH);
56 if (output[0] == 1) {
57 zassert_equal(memcmp(output, databuffer1, size32), 0);
58 } else {
59 zassert_equal(memcmp(output, databuffer2, size32), 0);
60 }
61 }
62
user_handler(void * user_data,uint32_t iter_cnt,bool last,int prio)63 static bool user_handler(void *user_data, uint32_t iter_cnt, bool last, int prio)
64 {
65 uintptr_t id = (uintptr_t)user_data;
66 uint32_t *buffer = id ? databuffer2 : databuffer1;
67
68 if (iter_cnt == 0) {
69 for (int i = 0; i < LENGTH; i++) {
70 buffer[i] = 1;
71 }
72 }
73
74 /* Try to write data into the ringbuffer */
75 data_write(buffer);
76 /* Try to get data from the ringbuffer and check */
77 data_read(data_output);
78
79 return true;
80 }
81
82 /**
83 * @brief Test that prevent concurrent writing
84 * operations by using a mutex
85 *
86 * @details Define a ring buffer and a mutex,
87 * and then spawn two threads to read and
88 * write the same buffer at the same time to
89 * check the integrity of data reading and writing.
90 *
91 * @ingroup lib_ringbuffer_tests
92 */
ZTEST(ringbuffer_api,test_ringbuffer_concurrent)93 ZTEST(ringbuffer_api, test_ringbuffer_concurrent)
94 {
95 ztress_set_timeout(K_MSEC(1000));
96 ZTRESS_EXECUTE(ZTRESS_THREAD(user_handler, (void *)0, 0, 0, Z_TIMEOUT_TICKS(20)),
97 ZTRESS_THREAD(user_handler, (void *)1, 0, 10, Z_TIMEOUT_TICKS(20)));
98 }
99
produce_cpy(void * user_data,uint32_t iter_cnt,bool last,int prio)100 static bool produce_cpy(void *user_data, uint32_t iter_cnt, bool last, int prio)
101 {
102 static int cnt;
103 uint8_t buf[3];
104 uint32_t len;
105
106 if (iter_cnt == 0) {
107 cnt = 0;
108 }
109
110 for (int i = 0; i < sizeof(buf); i++) {
111 buf[i] = (uint8_t)cnt++;
112 }
113
114 len = ring_buf_put(&ringbuf, buf, sizeof(buf));
115 cnt -= (sizeof(buf) - len);
116
117 return true;
118 }
119
consume_cpy(void * user_data,uint32_t iter_cnt,bool last,int prio)120 static bool consume_cpy(void *user_data, uint32_t iter_cnt, bool last, int prio)
121 {
122 static int cnt;
123 uint8_t buf[3];
124 uint32_t len;
125
126 if (iter_cnt == 0) {
127 cnt = 0;
128 }
129
130 len = ring_buf_get(&ringbuf, buf, sizeof(buf));
131 for (int i = 0; i < len; i++) {
132 zassert_equal(buf[i], (uint8_t)cnt);
133 cnt++;
134 }
135
136 return true;
137 }
138
produce_item(void * user_data,uint32_t cnt,bool last,int prio)139 static bool produce_item(void *user_data, uint32_t cnt, bool last, int prio)
140 {
141 int err;
142 static uint32_t pcnt;
143 uint32_t buf[2];
144
145 if (cnt == 0) {
146 pcnt = 0;
147 }
148
149 err = ring_buf_item_put(&ringbuf, (uint16_t)pcnt, VALUE, buf, 2);
150 if (err == 0) {
151 pcnt++;
152 }
153
154 return true;
155 }
156
consume_item(void * user_data,uint32_t cnt,bool last,int prio)157 static bool consume_item(void *user_data, uint32_t cnt, bool last, int prio)
158 {
159 int err;
160 static uint32_t pcnt;
161 uint32_t data[2];
162 uint16_t type;
163 uint8_t value;
164 uint8_t size32 = ARRAY_SIZE(data);
165
166 if (cnt == 0) {
167 pcnt = 0;
168 }
169
170 err = ring_buf_item_get(&ringbuf, &type, &value, data, &size32);
171 if (err == 0) {
172 zassert_equal(value, VALUE);
173 zassert_equal(type, (uint16_t)pcnt);
174 pcnt++;
175 } else if (err == -EMSGSIZE) {
176 zassert_true(false);
177 }
178
179 return true;
180 }
181
produce(void * user_data,uint32_t iter_cnt,bool last,int prio)182 static bool produce(void *user_data, uint32_t iter_cnt, bool last, int prio)
183 {
184 static int cnt;
185 static int wr = 8;
186 uint32_t len;
187 uint8_t *data;
188
189 if (iter_cnt == 0) {
190 cnt = 0;
191 }
192
193 len = ring_buf_put_claim(&ringbuf, &data, wr);
194 if (len == 0) {
195 len = ring_buf_put_claim(&ringbuf, &data, wr);
196 }
197
198 if (len == 0) {
199 return true;
200 }
201
202 for (uint32_t i = 0; i < len; i++) {
203 data[i] = cnt++;
204 }
205
206 wr++;
207 if (wr == 15) {
208 wr = 8;
209 }
210
211 int err = ring_buf_put_finish(&ringbuf, len);
212
213 zassert_equal(err, 0, "cnt: %d", cnt);
214
215 return true;
216 }
217
consume(void * user_data,uint32_t iter_cnt,bool last,int prio)218 static bool consume(void *user_data, uint32_t iter_cnt, bool last, int prio)
219 {
220 static int rd = 8;
221 static int cnt;
222 uint32_t len;
223 uint8_t *data;
224
225 if (iter_cnt == 0) {
226 cnt = 0;
227 }
228
229 len = ring_buf_get_claim(&ringbuf, &data, rd);
230 if (len == 0) {
231 len = ring_buf_get_claim(&ringbuf, &data, rd);
232 }
233
234 if (len == 0) {
235 return true;
236 }
237
238 for (uint32_t i = 0; i < len; i++) {
239 zassert_equal(data[i], (uint8_t)cnt,
240 "Got %02x, exp: %02x", data[i], (uint8_t)cnt);
241 cnt++;
242 }
243
244 rd++;
245 if (rd == 15) {
246 rd = 8;
247 }
248
249 int err = ring_buf_get_finish(&ringbuf, len);
250
251 zassert_equal(err, 0);
252
253 return true;
254 }
255
test_ztress(ztress_handler high_handler,ztress_handler low_handler,bool item_mode)256 static void test_ztress(ztress_handler high_handler,
257 ztress_handler low_handler,
258 bool item_mode)
259 {
260 union {
261 uint8_t buf8[32];
262 uint32_t buf32[32];
263 } buf;
264 k_timeout_t timeout;
265 int32_t offset;
266
267 if (item_mode) {
268 ring_buf_item_init(&ringbuf, ARRAY_SIZE(buf.buf32), buf.buf32);
269 } else {
270 ring_buf_init(&ringbuf, ARRAY_SIZE(buf.buf8), buf.buf8);
271 }
272
273 /* force internal 32-bit index roll-over */
274 offset = INT32_MAX - ring_buf_capacity_get(&ringbuf)/2;
275 ring_buf_internal_reset(&ringbuf, offset);
276
277 /* Timeout after 5 seconds. */
278 timeout = (CONFIG_SYS_CLOCK_TICKS_PER_SEC < 10000) ? K_MSEC(1000) : K_MSEC(10000);
279
280 ztress_set_timeout(timeout);
281 ZTRESS_EXECUTE(ZTRESS_THREAD(high_handler, NULL, 0, 0, Z_TIMEOUT_TICKS(20)),
282 ZTRESS_THREAD(low_handler, NULL, 0, 2000, Z_TIMEOUT_TICKS(20)));
283 }
284
test_ringbuffer_stress(ztress_handler produce_handler,ztress_handler consume_handler,bool item_mode)285 void test_ringbuffer_stress(ztress_handler produce_handler,
286 ztress_handler consume_handler,
287 bool item_mode)
288 {
289 PRINT("Producing interrupts consuming\n");
290 test_ztress(produce_handler, consume_handler, item_mode);
291
292 PRINT("Consuming interrupts producing\n");
293 test_ztress(consume_handler, produce_handler, item_mode);
294 }
295
296 /* Zero-copy API. Test is validating single producer, single consumer from
297 * different priorities.
298 */
ZTEST(ringbuffer_api,test_ringbuffer_zerocpy_stress)299 ZTEST(ringbuffer_api, test_ringbuffer_zerocpy_stress)
300 {
301 test_ringbuffer_stress(produce, consume, false);
302 }
303
304 /* Copy API. Test is validating single producer, single consumer from
305 * different priorities.
306 */
ZTEST(ringbuffer_api,test_ringbuffer_cpy_stress)307 ZTEST(ringbuffer_api, test_ringbuffer_cpy_stress)
308 {
309 test_ringbuffer_stress(produce_cpy, consume_cpy, false);
310 }
311
312 /* Item API. Test is validating single producer, single consumer from
313 * different priorities.
314 */
ZTEST(ringbuffer_api,test_ringbuffer_item_stress)315 ZTEST(ringbuffer_api, test_ringbuffer_item_stress)
316 {
317 test_ringbuffer_stress(produce_item, consume_item, true);
318 }
319