1 /*
2  * Copyright (c) 2024 Croxel Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/rtio/rtio.h>
10 #include <zephyr/rtio/work.h>
11 
12 /** Used to validate/control test execution flow */
13 K_SEM_DEFINE(work_handler_sem_1, 0, 1);
14 K_SEM_DEFINE(work_handler_sem_2, 0, 1);
15 K_SEM_DEFINE(work_handler_sem_3, 0, 1);
16 static int work_handler_called;
17 
work_handler(struct rtio_iodev_sqe * iodev_sqe)18 static void work_handler(struct rtio_iodev_sqe *iodev_sqe)
19 {
20 	struct rtio_sqe *sqe = &iodev_sqe->sqe;
21 	struct k_sem *sem = (struct k_sem *)sqe->userdata;
22 
23 	work_handler_called++;
24 	printk("\t- %s() called!: %d\n", __func__, work_handler_called);
25 
26 	k_sem_take(sem, K_FOREVER);
27 
28 	rtio_executor_ok(iodev_sqe, 0);
29 }
30 
dummy_submit(struct rtio_iodev_sqe * iodev_sqe)31 static void dummy_submit(struct rtio_iodev_sqe *iodev_sqe)
32 {
33 	struct rtio_work_req *req = rtio_work_req_alloc();
34 
35 	rtio_work_req_submit(req, iodev_sqe, work_handler);
36 }
37 
38 struct rtio_iodev_api r_iodev_test_api = {
39 	.submit = dummy_submit,
40 };
41 
42 RTIO_IODEV_DEFINE(dummy_iodev, &r_iodev_test_api, NULL);
43 RTIO_IODEV_DEFINE(dummy_iodev_2, &r_iodev_test_api, NULL);
44 RTIO_IODEV_DEFINE(dummy_iodev_3, &r_iodev_test_api, NULL);
45 
46 RTIO_DEFINE(r_test, 3, 3);
47 RTIO_DEFINE(r_test_2, 3, 3);
48 RTIO_DEFINE(r_test_3, 3, 3);
49 
before(void * unused)50 static void before(void *unused)
51 {
52 	rtio_sqe_drop_all(&r_test);
53 	rtio_sqe_drop_all(&r_test_2);
54 	rtio_sqe_drop_all(&r_test_3);
55 
56 	k_sem_init(&work_handler_sem_1, 0, 1);
57 	k_sem_init(&work_handler_sem_2, 0, 1);
58 	k_sem_init(&work_handler_sem_3, 0, 1);
59 
60 	work_handler_called = 0;
61 }
62 
after(void * unused)63 static void after(void *unused)
64 {
65 }
66 
67 ZTEST_SUITE(rtio_work, NULL, NULL, before, after, NULL);
68 
ZTEST(rtio_work,test_work_decouples_submission)69 ZTEST(rtio_work, test_work_decouples_submission)
70 {
71 	struct rtio_sqe *sqe;
72 	struct rtio_cqe *cqe;
73 
74 	sqe = rtio_sqe_acquire(&r_test);
75 	rtio_sqe_prep_nop(sqe, &dummy_iodev, &work_handler_sem_1);
76 	sqe->prio = RTIO_PRIO_NORM;
77 
78 	zassert_equal(0, work_handler_called);
79 	zassert_equal(0, rtio_work_req_used_count_get());
80 
81 	zassert_ok(rtio_submit(&r_test, 0));
82 
83 	zassert_equal(1, work_handler_called);
84 	zassert_equal(1, rtio_work_req_used_count_get());
85 
86 	k_sem_give(&work_handler_sem_1);
87 	zassert_equal(0, rtio_work_req_used_count_get());
88 
89 	/** Clean-up */
90 	cqe = rtio_cqe_consume_block(&r_test);
91 	rtio_cqe_release(&r_test, cqe);
92 }
93 
ZTEST(rtio_work,test_work_supports_batching_submissions)94 ZTEST(rtio_work, test_work_supports_batching_submissions)
95 {
96 	struct rtio_sqe *sqe_a;
97 	struct rtio_sqe *sqe_b;
98 	struct rtio_sqe *sqe_c;
99 	struct rtio_cqe *cqe;
100 
101 	sqe_a = rtio_sqe_acquire(&r_test);
102 	rtio_sqe_prep_nop(sqe_a, &dummy_iodev, &work_handler_sem_1);
103 	sqe_a->prio = RTIO_PRIO_NORM;
104 
105 	sqe_b = rtio_sqe_acquire(&r_test);
106 	rtio_sqe_prep_nop(sqe_b, &dummy_iodev, &work_handler_sem_2);
107 	sqe_b->prio = RTIO_PRIO_NORM;
108 
109 	sqe_c = rtio_sqe_acquire(&r_test);
110 	rtio_sqe_prep_nop(sqe_c, &dummy_iodev, &work_handler_sem_3);
111 	sqe_c->prio = RTIO_PRIO_NORM;
112 
113 	zassert_ok(rtio_submit(&r_test, 0));
114 
115 	k_sem_give(&work_handler_sem_1);
116 	k_sem_give(&work_handler_sem_2);
117 	k_sem_give(&work_handler_sem_3);
118 
119 	zassert_equal(3, work_handler_called);
120 	zassert_equal(0, rtio_work_req_used_count_get());
121 
122 	/** Clean-up */
123 	cqe = rtio_cqe_consume_block(&r_test);
124 	rtio_cqe_release(&r_test, cqe);
125 	cqe = rtio_cqe_consume_block(&r_test);
126 	rtio_cqe_release(&r_test, cqe);
127 	cqe = rtio_cqe_consume_block(&r_test);
128 	rtio_cqe_release(&r_test, cqe);
129 }
130 
ZTEST(rtio_work,test_work_supports_preempting_on_higher_prio_submissions)131 ZTEST(rtio_work, test_work_supports_preempting_on_higher_prio_submissions)
132 {
133 	struct rtio_sqe *sqe_a;
134 	struct rtio_sqe *sqe_b;
135 	struct rtio_sqe *sqe_c;
136 	struct rtio_cqe *cqe;
137 
138 	sqe_a = rtio_sqe_acquire(&r_test);
139 	rtio_sqe_prep_nop(sqe_a, &dummy_iodev, &work_handler_sem_1);
140 	sqe_a->prio = RTIO_PRIO_LOW;
141 
142 	sqe_b = rtio_sqe_acquire(&r_test_2);
143 	rtio_sqe_prep_nop(sqe_b, &dummy_iodev_2, &work_handler_sem_2);
144 	sqe_b->prio = RTIO_PRIO_NORM;
145 
146 	sqe_c = rtio_sqe_acquire(&r_test_3);
147 	rtio_sqe_prep_nop(sqe_c, &dummy_iodev_3, &work_handler_sem_3);
148 	sqe_c->prio = RTIO_PRIO_HIGH;
149 
150 	zassert_ok(rtio_submit(&r_test, 0));
151 	zassert_ok(rtio_submit(&r_test_2, 0));
152 	zassert_ok(rtio_submit(&r_test_3, 0));
153 
154 	zassert_equal(3, work_handler_called);
155 	zassert_equal(3, rtio_work_req_used_count_get());
156 
157 	k_sem_give(&work_handler_sem_1);
158 	k_sem_give(&work_handler_sem_2);
159 	k_sem_give(&work_handler_sem_3);
160 
161 	zassert_equal(3, work_handler_called);
162 	zassert_equal(0, rtio_work_req_used_count_get());
163 
164 	/** Clean-up */
165 	cqe = rtio_cqe_consume_block(&r_test);
166 	rtio_cqe_release(&r_test, cqe);
167 	cqe = rtio_cqe_consume_block(&r_test_2);
168 	rtio_cqe_release(&r_test_2, cqe);
169 	cqe = rtio_cqe_consume_block(&r_test_3);
170 	rtio_cqe_release(&r_test_3, cqe);
171 }
172 
ZTEST(rtio_work,test_used_count_keeps_track_of_alloc_items)173 ZTEST(rtio_work, test_used_count_keeps_track_of_alloc_items)
174 {
175 	struct rtio_work_req *req_a = NULL;
176 	struct rtio_work_req *req_b = NULL;
177 	struct rtio_work_req *req_c = NULL;
178 	struct rtio_work_req *req_d = NULL;
179 	struct rtio_work_req *req_e = NULL;
180 
181 	zassert_equal(0, rtio_work_req_used_count_get());
182 
183 	/** We expect valid items and the count kept track */
184 	req_a = rtio_work_req_alloc();
185 	zassert_not_null(req_a);
186 	zassert_equal(1, rtio_work_req_used_count_get());
187 
188 	req_b = rtio_work_req_alloc();
189 	zassert_not_null(req_b);
190 	zassert_equal(2, rtio_work_req_used_count_get());
191 
192 	req_c = rtio_work_req_alloc();
193 	zassert_not_null(req_c);
194 	zassert_equal(3, rtio_work_req_used_count_get());
195 
196 	req_d = rtio_work_req_alloc();
197 	zassert_not_null(req_d);
198 	zassert_equal(4, rtio_work_req_used_count_get());
199 
200 	/** This time should not have been able to allocate. */
201 	req_e = rtio_work_req_alloc();
202 	zassert_is_null(req_e);
203 	zassert_equal(4, rtio_work_req_used_count_get());
204 
205 	/** Flush requests */
206 	rtio_work_req_submit(req_a, NULL, NULL);
207 	zassert_equal(3, rtio_work_req_used_count_get());
208 
209 	rtio_work_req_submit(req_b, NULL, NULL);
210 	zassert_equal(2, rtio_work_req_used_count_get());
211 
212 	rtio_work_req_submit(req_c, NULL, NULL);
213 	zassert_equal(1, rtio_work_req_used_count_get());
214 
215 	rtio_work_req_submit(req_d, NULL, NULL);
216 	zassert_equal(0, rtio_work_req_used_count_get());
217 }
218