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