1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/irq_offload.h>
9 #include <zephyr/sys/__assert.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(test);
13 
14 /*
15  * @file
16  * @brief Test fifo APIs timeout
17  *
18  * This module tests following fifo timeout scenarios
19  *
20  * First, the thread waits with a timeout and times out. Then it wait with a
21  * timeout, but gets the data in time.
22  *
23  * Then, multiple timeout tests are done for the threads, to test the ordering
24  * of queueing/dequeueing when timeout occurs, first on one fifo, then on
25  * multiple fifos.
26  *
27  * Finally, multiple threads pend on one fifo, and they all get the
28  * data in time, except the last one: this tests that the timeout is
29  * recomputed correctly when timeouts are aborted.
30  */
31 
32 struct scratch_fifo_packet {
33 	void *link_in_fifo;
34 	void *data_if_needed;
35 };
36 
37 struct reply_packet {
38 	void *link_in_fifo;
39 	int32_t reply;
40 };
41 
42 struct timeout_order_data {
43 	void *link_in_fifo;
44 	struct k_fifo *fifo;
45 	uint32_t timeout;
46 	int32_t timeout_order;
47 	int32_t q_order;
48 };
49 
50 
51 #define NUM_SCRATCH_FIFO_PACKETS 20
52 struct scratch_fifo_packet scratch_fifo_packets[NUM_SCRATCH_FIFO_PACKETS];
53 
54 struct k_fifo scratch_fifo_packets_fifo;
55 
56 static struct k_fifo fifo_timeout[2];
57 struct k_fifo timeout_order_fifo;
58 
59 struct timeout_order_data timeout_order_data[] = {
60 	{0, &fifo_timeout[0], 20, 2, 0},
61 	{0, &fifo_timeout[0], 40, 4, 1},
62 	{0, &fifo_timeout[0], 0, 0, 2},
63 	{0, &fifo_timeout[0], 10, 1, 3},
64 	{0, &fifo_timeout[0], 30, 3, 4},
65 };
66 
67 struct timeout_order_data timeout_order_data_mult_fifo[] = {
68 	{0, &fifo_timeout[1], 0, 0, 0},
69 	{0, &fifo_timeout[0], 30, 3, 1},
70 	{0, &fifo_timeout[0], 50, 5, 2},
71 	{0, &fifo_timeout[1], 80, 8, 3},
72 	{0, &fifo_timeout[1], 70, 7, 4},
73 	{0, &fifo_timeout[0], 10, 1, 5},
74 	{0, &fifo_timeout[0], 60, 6, 6},
75 	{0, &fifo_timeout[0], 20, 2, 7},
76 	{0, &fifo_timeout[1], 40, 4, 8},
77 };
78 
79 #define TIMEOUT_ORDER_NUM_THREADS	ARRAY_SIZE(timeout_order_data_mult_fifo)
80 #define TSTACK_SIZE			(512 + CONFIG_TEST_EXTRA_STACK_SIZE)
81 #define FIFO_THREAD_PRIO		-5
82 
83 static K_THREAD_STACK_ARRAY_DEFINE(ttstack,
84 		TIMEOUT_ORDER_NUM_THREADS, TSTACK_SIZE);
85 static struct k_thread ttdata[TIMEOUT_ORDER_NUM_THREADS];
86 static k_tid_t tid[TIMEOUT_ORDER_NUM_THREADS];
87 
get_scratch_packet(void)88 static void *get_scratch_packet(void)
89 {
90 	void *packet = k_fifo_get(&scratch_fifo_packets_fifo, K_NO_WAIT);
91 
92 	zassert_true(packet != NULL);
93 	return packet;
94 }
95 
put_scratch_packet(void * packet)96 static void put_scratch_packet(void *packet)
97 {
98 	k_fifo_put(&scratch_fifo_packets_fifo, packet);
99 }
100 
is_timeout_in_range(uint32_t start_time,uint32_t timeout)101 static bool is_timeout_in_range(uint32_t start_time, uint32_t timeout)
102 {
103 	uint32_t stop_time, diff;
104 
105 	stop_time = k_cycle_get_32();
106 	diff = (uint32_t)k_cyc_to_ns_floor64(stop_time -
107 			start_time) / NSEC_PER_USEC;
108 	diff = diff / USEC_PER_MSEC;
109 	return timeout <= diff;
110 }
111 
112 /* a thread sleeps then puts data on the fifo */
test_thread_put_timeout(void * p1,void * p2,void * p3)113 static void test_thread_put_timeout(void *p1, void *p2, void *p3)
114 {
115 	uint32_t timeout = *((uint32_t *)p2);
116 
117 	k_msleep(timeout);
118 	k_fifo_put((struct k_fifo *)p1, get_scratch_packet());
119 }
120 
121 /* a thread pends on a fifo then times out */
test_thread_pend_and_timeout(void * p1,void * p2,void * p3)122 static void test_thread_pend_and_timeout(void *p1, void *p2, void *p3)
123 {
124 	struct timeout_order_data *d = (struct timeout_order_data *)p1;
125 	uint32_t start_time;
126 	void *packet;
127 
128 	k_msleep(1); /* Align to ticks */
129 
130 	start_time = k_cycle_get_32();
131 	packet = k_fifo_get(d->fifo, K_MSEC(d->timeout));
132 	zassert_true(packet == NULL);
133 	zassert_true(is_timeout_in_range(start_time, d->timeout));
134 
135 	k_fifo_put(&timeout_order_fifo, d);
136 }
137 /* Spins several threads that pend and timeout on fifos */
test_multiple_threads_pending(struct timeout_order_data * test_data,int test_data_size)138 static int test_multiple_threads_pending(struct timeout_order_data *test_data,
139 					 int test_data_size)
140 {
141 	int ii, j;
142 	uint32_t diff_ms;
143 
144 	for (ii = 0; ii < test_data_size; ii++) {
145 		tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
146 				test_thread_pend_and_timeout,
147 				&test_data[ii], NULL, NULL,
148 				FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
149 	}
150 
151 	/* In general, there is no guarantee of wakeup order when multiple
152 	 * threads are woken up on the same tick. This can especially happen
153 	 * when the system is loaded. However, in this particular test, we
154 	 * are controlling the system state and hence we can make a reasonable
155 	 * estimation of a timeout occurring with the max deviation of an
156 	 * additional tick. Hence the timeout order may slightly be different
157 	 * from what we normally expect.
158 	 */
159 	for (ii = 0; ii < test_data_size; ii++) {
160 		struct timeout_order_data *data =
161 			k_fifo_get(&timeout_order_fifo, K_FOREVER);
162 
163 		zassert_not_null(data, NULL);
164 		if (data->timeout_order == ii) {
165 			LOG_DBG(" thread (q order: %d, t/o: %d, fifo %p)",
166 				data->q_order, data->timeout, data->fifo);
167 		} else {
168 			/* Get the index of the thread which should have
169 			 * actually timed out.
170 			 */
171 			for (j = 0; j < test_data_size; j++) {
172 				if (test_data[j].timeout_order == ii) {
173 					break;
174 				}
175 			}
176 
177 			if (data->timeout > test_data[j].timeout) {
178 				diff_ms = data->timeout - test_data[j].timeout;
179 			} else {
180 				diff_ms = test_data[j].timeout - data->timeout;
181 			}
182 
183 			if (k_ms_to_ticks_ceil32(diff_ms) == 1) {
184 				LOG_DBG(
185 				" thread (q order: %d, t/o: %d, fifo %p)",
186 				data->q_order, data->timeout, data->fifo);
187 			} else {
188 				TC_ERROR(
189 				" *** thread %d woke up, expected %d\n",
190 				data->q_order, j);
191 				return TC_FAIL;
192 			}
193 		}
194 	}
195 
196 	return TC_PASS;
197 }
198 
199 /* a thread pends on a fifo with a timeout and gets the data in time */
test_thread_pend_and_get_data(void * p1,void * p2,void * p3)200 static void test_thread_pend_and_get_data(void *p1, void *p2, void *p3)
201 {
202 	struct timeout_order_data *d = (struct timeout_order_data *)p1;
203 	void *packet;
204 
205 	packet = k_fifo_get(d->fifo, K_MSEC(d->timeout));
206 	zassert_true(packet != NULL);
207 
208 	put_scratch_packet(packet);
209 	k_fifo_put(&timeout_order_fifo, d);
210 }
211 
212 /* Spins child threads that get fifo data in time, except the last one */
test_multiple_threads_get_data(struct timeout_order_data * test_data,int test_data_size)213 static int test_multiple_threads_get_data(struct timeout_order_data *test_data,
214 					 int test_data_size)
215 {
216 	struct timeout_order_data *data;
217 	int ii;
218 
219 	for (ii = 0; ii < test_data_size-1; ii++) {
220 		tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
221 				test_thread_pend_and_get_data,
222 				&test_data[ii], NULL, NULL,
223 				K_PRIO_PREEMPT(0), K_INHERIT_PERMS, K_NO_WAIT);
224 	}
225 
226 	tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
227 				test_thread_pend_and_timeout,
228 				&test_data[ii], NULL, NULL,
229 				K_PRIO_PREEMPT(0), K_INHERIT_PERMS, K_NO_WAIT);
230 
231 	for (ii = 0; ii < test_data_size-1; ii++) {
232 		k_fifo_put(test_data[ii].fifo, get_scratch_packet());
233 
234 		data = k_fifo_get(&timeout_order_fifo, K_FOREVER);
235 		if (!data) {
236 			TC_ERROR("thread %d got NULL value from fifo\n", ii);
237 			return TC_FAIL;
238 		}
239 
240 		if (data->q_order != ii) {
241 			TC_ERROR(" *** thread %d woke up, expected %d\n",
242 				 data->q_order, ii);
243 			return TC_FAIL;
244 		}
245 
246 		if (data->q_order == ii) {
247 			LOG_DBG(" thread (q order: %d, t/o: %d, fifo %p)",
248 				data->q_order, data->timeout, data->fifo);
249 		}
250 	}
251 
252 	data = k_fifo_get(&timeout_order_fifo, K_FOREVER);
253 	if (!data) {
254 		TC_ERROR("thread %d got NULL value from fifo\n", ii);
255 		return TC_FAIL;
256 	}
257 
258 	if (data->q_order != ii) {
259 		TC_ERROR(" *** thread %d woke up, expected %d\n",
260 			 data->q_order, ii);
261 		return TC_FAIL;
262 	}
263 
264 	LOG_DBG(" thread (q order: %d, t/o: %d, fifo %p)",
265 		data->q_order, data->timeout, data->fifo);
266 
267 	return TC_PASS;
268 }
269 
270 /* try getting data on fifo with special timeout value, return result in fifo */
test_thread_timeout_reply_values(void * p1,void * p2,void * p3)271 static void test_thread_timeout_reply_values(void *p1, void *p2, void *p3)
272 {
273 	struct reply_packet *reply_packet = (struct reply_packet *)p1;
274 
275 	reply_packet->reply =
276 		!!k_fifo_get(&fifo_timeout[0], K_NO_WAIT);
277 
278 	k_fifo_put(&timeout_order_fifo, reply_packet);
279 }
280 
test_thread_timeout_reply_values_wfe(void * p1,void * p2,void * p3)281 static void test_thread_timeout_reply_values_wfe(void *p1, void *p2, void *p3)
282 {
283 	struct reply_packet *reply_packet = (struct reply_packet *)p1;
284 
285 	reply_packet->reply =
286 		!!k_fifo_get(&fifo_timeout[0], K_FOREVER);
287 
288 	k_fifo_put(&timeout_order_fifo, reply_packet);
289 }
290 
291 /**
292  * @addtogroup kernel_fifo_tests
293  * @{
294  */
295 
296 /**
297  * @brief Test empty fifo with timeout and K_NO_WAIT
298  * @see k_fifo_get()
299  */
ZTEST(fifo_timeout_1cpu,test_timeout_empty_fifo)300 ZTEST(fifo_timeout_1cpu, test_timeout_empty_fifo)
301 {
302 	void *packet;
303 	uint32_t start_time, timeout;
304 
305 	k_msleep(1); /* Align to ticks */
306 
307 	/* Test empty fifo with timeout */
308 	timeout = 10U;
309 	start_time = k_cycle_get_32();
310 	packet = k_fifo_get(&fifo_timeout[0], K_MSEC(timeout));
311 	zassert_true(packet == NULL);
312 	zassert_true(is_timeout_in_range(start_time, timeout));
313 
314 	/* Test empty fifo with timeout of K_NO_WAIT */
315 	packet = k_fifo_get(&fifo_timeout[0], K_NO_WAIT);
316 	zassert_true(packet == NULL);
317 }
318 
319 /**
320  * @brief Test non empty fifo with timeout and K_NO_WAIT
321  * @see k_fifo_get(), k_fifo_put()
322  */
ZTEST(fifo_timeout,test_timeout_non_empty_fifo)323 ZTEST(fifo_timeout, test_timeout_non_empty_fifo)
324 {
325 	void *packet, *scratch_packet;
326 
327 	 /* Test k_fifo_get with K_NO_WAIT */
328 	scratch_packet = get_scratch_packet();
329 	k_fifo_put(&fifo_timeout[0], scratch_packet);
330 	packet = k_fifo_get(&fifo_timeout[0], K_NO_WAIT);
331 	zassert_true(packet != NULL);
332 	put_scratch_packet(scratch_packet);
333 
334 	 /* Test k_fifo_get with K_FOREVER */
335 	scratch_packet = get_scratch_packet();
336 	k_fifo_put(&fifo_timeout[0], scratch_packet);
337 	packet = k_fifo_get(&fifo_timeout[0], K_FOREVER);
338 	zassert_true(packet != NULL);
339 	put_scratch_packet(scratch_packet);
340 }
341 
342 /**
343  * @brief Test fifo with timeout and K_NO_WAIT
344  * @details In first scenario test fifo with some timeout where child thread
345  * puts data on the fifo on time. In second scenario test k_fifo_get with
346  * timeout of K_NO_WAIT and the fifo should be filled by the child thread
347  * based on the data availability on another fifo. In third scenario test
348  * k_fifo_get with timeout of K_FOREVER and the fifo should be filled by
349  * the child thread based on the data availability on another fifo.
350  * @see k_fifo_get(), k_fifo_put()
351  */
ZTEST(fifo_timeout_1cpu,test_timeout_fifo_thread)352 ZTEST(fifo_timeout_1cpu, test_timeout_fifo_thread)
353 {
354 	void *packet, *scratch_packet;
355 	struct reply_packet reply_packet;
356 	uint32_t start_time, timeout;
357 
358 	k_msleep(1); /* Align to ticks */
359 
360 	/*
361 	 * Test fifo with some timeout and child thread that puts
362 	 * data on the fifo on time
363 	 */
364 	timeout = 10U;
365 	start_time = k_cycle_get_32();
366 
367 	tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
368 				test_thread_put_timeout, &fifo_timeout[0],
369 				&timeout, NULL,
370 				FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
371 
372 	packet = k_fifo_get(&fifo_timeout[0], K_MSEC(timeout + 10));
373 	zassert_true(packet != NULL);
374 	zassert_true(is_timeout_in_range(start_time, timeout));
375 	put_scratch_packet(packet);
376 
377 	/*
378 	 * Test k_fifo_get with timeout of K_NO_WAIT and the fifo
379 	 * should be filled be filled by the child thread based on
380 	 * the data availability on another fifo. In this test child
381 	 * thread does not find data on fifo.
382 	 */
383 	tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
384 				test_thread_timeout_reply_values,
385 				&reply_packet, NULL, NULL,
386 				FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
387 
388 	k_yield();
389 	packet = k_fifo_get(&timeout_order_fifo, K_NO_WAIT);
390 	zassert_true(packet != NULL);
391 	zassert_false(reply_packet.reply);
392 
393 	/*
394 	 * Test k_fifo_get with timeout of K_NO_WAIT and the fifo
395 	 * should be filled be filled by the child thread based on
396 	 * the data availability on another fifo. In this test child
397 	 * thread does find data on fifo.
398 	 */
399 	scratch_packet = get_scratch_packet();
400 	k_fifo_put(&fifo_timeout[0], scratch_packet);
401 
402 	tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
403 				test_thread_timeout_reply_values,
404 				&reply_packet, NULL, NULL,
405 				FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
406 
407 	k_yield();
408 	packet = k_fifo_get(&timeout_order_fifo, K_NO_WAIT);
409 	zassert_true(packet != NULL);
410 	zassert_true(reply_packet.reply);
411 	put_scratch_packet(scratch_packet);
412 
413 	/*
414 	 * Test k_fifo_get with timeout of K_FOREVER and the fifo
415 	 * should be filled be filled by the child thread based on
416 	 * the data availability on another fifo. In this test child
417 	 * thread does find data on fifo.
418 	 */
419 	scratch_packet = get_scratch_packet();
420 	k_fifo_put(&fifo_timeout[0], scratch_packet);
421 
422 	tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
423 				test_thread_timeout_reply_values_wfe,
424 				&reply_packet, NULL, NULL,
425 				FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
426 
427 	packet = k_fifo_get(&timeout_order_fifo, K_FOREVER);
428 	zassert_true(packet != NULL);
429 	zassert_true(reply_packet.reply);
430 	put_scratch_packet(scratch_packet);
431 }
432 
433 /**
434  * @brief Test fifo with different timeouts
435  * @details test multiple threads pending on the same fifo with
436  * different timeouts
437  * @see k_fifo_get(), k_fifo_put()
438  */
ZTEST(fifo_timeout_1cpu,test_timeout_threads_pend_on_fifo)439 ZTEST(fifo_timeout_1cpu, test_timeout_threads_pend_on_fifo)
440 {
441 	int32_t rv, test_data_size;
442 
443 	/*
444 	 * Test multiple threads pending on the same
445 	 * fifo with different timeouts
446 	 */
447 	test_data_size = ARRAY_SIZE(timeout_order_data);
448 	rv = test_multiple_threads_pending(timeout_order_data, test_data_size);
449 	zassert_equal(rv, TC_PASS);
450 }
451 
452 /**
453  * @brief Test multiple fifos with different timeouts
454  * @details test multiple threads pending on different fifos
455  * with different timeouts
456  * @see k_fifo_get(), k_fifo_put()
457  */
ZTEST(fifo_timeout_1cpu,test_timeout_threads_pend_on_dual_fifos)458 ZTEST(fifo_timeout_1cpu, test_timeout_threads_pend_on_dual_fifos)
459 {
460 	int32_t rv, test_data_size;
461 
462 	/*
463 	 * Test multiple threads pending on different
464 	 * fifos with different timeouts
465 	 */
466 	test_data_size = ARRAY_SIZE(timeout_order_data_mult_fifo);
467 	rv = test_multiple_threads_pending(timeout_order_data_mult_fifo,
468 							test_data_size);
469 	zassert_equal(rv, TC_PASS);
470 
471 }
472 
473 /**
474  * @brief Test same fifo with different timeouts
475  * @details test multiple threads pending on the same fifo with
476  * different timeouts but getting the data in time
477  * @see k_fifo_get(), k_fifo_put()
478  */
ZTEST(fifo_timeout_1cpu,test_timeout_threads_pend_fail_on_fifo)479 ZTEST(fifo_timeout_1cpu, test_timeout_threads_pend_fail_on_fifo)
480 {
481 	int32_t rv, test_data_size;
482 
483 	/*
484 	 * Test multiple threads pending on same
485 	 * fifo with different timeouts, but getting
486 	 * the data in time, except the last one.
487 	 */
488 	test_data_size = ARRAY_SIZE(timeout_order_data);
489 	rv = test_multiple_threads_get_data(timeout_order_data, test_data_size);
490 	zassert_equal(rv, TC_PASS);
491 }
492 
493 /**
494  * @brief Test fifo init
495  * @see k_fifo_init(), k_fifo_put()
496  */
test_timeout_setup(void)497 static void *test_timeout_setup(void)
498 {
499 	intptr_t ii;
500 
501 	/* Init kernel objects */
502 	k_fifo_init(&fifo_timeout[0]);
503 	k_fifo_init(&fifo_timeout[1]);
504 	k_fifo_init(&timeout_order_fifo);
505 	k_fifo_init(&scratch_fifo_packets_fifo);
506 
507 	/* Fill scratch fifo */
508 	for (ii = 0; ii < NUM_SCRATCH_FIFO_PACKETS; ii++) {
509 		scratch_fifo_packets[ii].data_if_needed = (void *)ii;
510 		k_fifo_put(&scratch_fifo_packets_fifo,
511 				(void *)&scratch_fifo_packets[ii]);
512 	}
513 
514 	return NULL;
515 }
516 /**
517  * @}
518  */
519 
520 ZTEST_SUITE(fifo_timeout, NULL, test_timeout_setup, NULL, NULL, NULL);
521 
522 ZTEST_SUITE(fifo_timeout_1cpu, NULL, test_timeout_setup,
523 		ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
524