xref: /spdk/test/unit/lib/event/reactor.c/reactor_ut.c (revision d73077b84a71985da1db1c9847ea7c042189bae2)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk_cunit.h"
37 #include "common/lib/test_env.c"
38 #include "event/reactor.c"
39 #include "spdk_internal/thread.h"
40 #include "event/scheduler_static.c"
41 
42 static void
43 test_create_reactor(void)
44 {
45 	struct spdk_reactor reactor = {};
46 
47 	g_reactors = &reactor;
48 
49 	reactor_construct(&reactor, 0);
50 
51 	CU_ASSERT(spdk_reactor_get(0) == &reactor);
52 
53 	spdk_ring_free(reactor.events);
54 	g_reactors = NULL;
55 }
56 
57 static void
58 test_init_reactors(void)
59 {
60 	uint32_t core;
61 
62 	allocate_cores(3);
63 
64 	CU_ASSERT(spdk_reactors_init() == 0);
65 
66 	CU_ASSERT(g_reactor_state == SPDK_REACTOR_STATE_INITIALIZED);
67 	for (core = 0; core < 3; core++) {
68 		CU_ASSERT(spdk_reactor_get(core) != NULL);
69 	}
70 
71 	spdk_reactors_fini();
72 
73 	free_cores();
74 }
75 
76 static void
77 ut_event_fn(void *arg1, void *arg2)
78 {
79 	uint8_t *test1 = arg1;
80 	uint8_t *test2 = arg2;
81 
82 	*test1 = 1;
83 	*test2 = 0xFF;
84 }
85 
86 static void
87 test_event_call(void)
88 {
89 	uint8_t test1 = 0, test2 = 0;
90 	struct spdk_event *evt;
91 	struct spdk_reactor *reactor;
92 
93 	allocate_cores(1);
94 
95 	CU_ASSERT(spdk_reactors_init() == 0);
96 
97 	evt = spdk_event_allocate(0, ut_event_fn, &test1, &test2);
98 	CU_ASSERT(evt != NULL);
99 
100 	spdk_event_call(evt);
101 
102 	reactor = spdk_reactor_get(0);
103 	CU_ASSERT(reactor != NULL);
104 
105 	CU_ASSERT(event_queue_run_batch(reactor) == 1);
106 	CU_ASSERT(test1 == 1);
107 	CU_ASSERT(test2 == 0xFF);
108 
109 	spdk_reactors_fini();
110 
111 	free_cores();
112 }
113 
114 static void
115 test_schedule_thread(void)
116 {
117 	struct spdk_cpuset cpuset = {};
118 	struct spdk_thread *thread;
119 	struct spdk_reactor *reactor;
120 	struct spdk_lw_thread *lw_thread;
121 
122 	allocate_cores(5);
123 
124 	CU_ASSERT(spdk_reactors_init() == 0);
125 
126 	spdk_cpuset_set_cpu(&cpuset, 3, true);
127 	g_next_core = 4;
128 
129 	/* _reactor_schedule_thread() will be called in spdk_thread_create()
130 	 * at its end because it is passed to SPDK thread library by
131 	 * spdk_thread_lib_init().
132 	 */
133 	thread = spdk_thread_create(NULL, &cpuset);
134 	CU_ASSERT(thread != NULL);
135 
136 	reactor = spdk_reactor_get(3);
137 	CU_ASSERT(reactor != NULL);
138 
139 	MOCK_SET(spdk_env_get_current_core, 3);
140 
141 	CU_ASSERT(event_queue_run_batch(reactor) == 1);
142 
143 	MOCK_CLEAR(spdk_env_get_current_core);
144 
145 	lw_thread = TAILQ_FIRST(&reactor->threads);
146 	CU_ASSERT(lw_thread != NULL);
147 	CU_ASSERT(spdk_thread_get_from_ctx(lw_thread) == thread);
148 
149 	TAILQ_REMOVE(&reactor->threads, lw_thread, link);
150 	reactor->thread_count--;
151 	spdk_set_thread(thread);
152 	spdk_thread_exit(thread);
153 	while (!spdk_thread_is_exited(thread)) {
154 		spdk_thread_poll(thread, 0, 0);
155 	}
156 	spdk_thread_destroy(thread);
157 	spdk_set_thread(NULL);
158 
159 	spdk_reactors_fini();
160 
161 	free_cores();
162 }
163 
164 static void
165 test_reschedule_thread(void)
166 {
167 	struct spdk_cpuset cpuset = {};
168 	struct spdk_thread *thread;
169 	struct spdk_reactor *reactor;
170 	struct spdk_lw_thread *lw_thread;
171 
172 	allocate_cores(3);
173 
174 	CU_ASSERT(spdk_reactors_init() == 0);
175 
176 	spdk_cpuset_set_cpu(&g_reactor_core_mask, 0, true);
177 	spdk_cpuset_set_cpu(&g_reactor_core_mask, 1, true);
178 	spdk_cpuset_set_cpu(&g_reactor_core_mask, 2, true);
179 	g_next_core = 0;
180 
181 	/* Create and schedule the thread to core 1. */
182 	spdk_cpuset_set_cpu(&cpuset, 1, true);
183 
184 	thread = spdk_thread_create(NULL, &cpuset);
185 	CU_ASSERT(thread != NULL);
186 	lw_thread = spdk_thread_get_ctx(thread);
187 
188 	reactor = spdk_reactor_get(1);
189 	CU_ASSERT(reactor != NULL);
190 	MOCK_SET(spdk_env_get_current_core, 1);
191 
192 	CU_ASSERT(event_queue_run_batch(reactor) == 1);
193 	CU_ASSERT(TAILQ_FIRST(&reactor->threads) == lw_thread);
194 
195 	spdk_set_thread(thread);
196 
197 	/* Call spdk_thread_set_cpumask() twice with different cpumask values.
198 	 * The cpumask of the 2nd call will be used in reschedule operation.
199 	 */
200 
201 	spdk_cpuset_zero(&cpuset);
202 	spdk_cpuset_set_cpu(&cpuset, 0, true);
203 	CU_ASSERT(spdk_thread_set_cpumask(&cpuset) == 0);
204 
205 	spdk_cpuset_zero(&cpuset);
206 	spdk_cpuset_set_cpu(&cpuset, 2, true);
207 	CU_ASSERT(spdk_thread_set_cpumask(&cpuset) == 0);
208 
209 	CU_ASSERT(lw_thread->resched == true);
210 
211 	reactor_run(reactor);
212 
213 	CU_ASSERT(lw_thread->resched == false);
214 	CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
215 
216 	reactor = spdk_reactor_get(0);
217 	CU_ASSERT(reactor != NULL);
218 	MOCK_SET(spdk_env_get_current_core, 0);
219 
220 	CU_ASSERT(event_queue_run_batch(reactor) == 0);
221 
222 	reactor = spdk_reactor_get(2);
223 	CU_ASSERT(reactor != NULL);
224 	MOCK_SET(spdk_env_get_current_core, 2);
225 
226 	CU_ASSERT(event_queue_run_batch(reactor) == 1);
227 
228 	CU_ASSERT(TAILQ_FIRST(&reactor->threads) == lw_thread);
229 
230 	MOCK_CLEAR(spdk_env_get_current_core);
231 
232 	TAILQ_REMOVE(&reactor->threads, lw_thread, link);
233 	reactor->thread_count--;
234 	spdk_set_thread(thread);
235 	spdk_thread_exit(thread);
236 	while (!spdk_thread_is_exited(thread)) {
237 		spdk_thread_poll(thread, 0, 0);
238 	}
239 	spdk_thread_destroy(thread);
240 	spdk_set_thread(NULL);
241 
242 	spdk_reactors_fini();
243 
244 	free_cores();
245 }
246 
247 static void
248 for_each_reactor_done(void *arg1, void *arg2)
249 {
250 	uint32_t *count = arg1;
251 	bool *done = arg2;
252 
253 	(*count)++;
254 	*done = true;
255 }
256 
257 static void
258 for_each_reactor_cb(void *arg1, void *arg2)
259 {
260 	uint32_t *count = arg1;
261 
262 	(*count)++;
263 }
264 
265 static void
266 test_for_each_reactor(void)
267 {
268 	uint32_t count = 0, i;
269 	bool done = false;
270 	struct spdk_reactor *reactor;
271 
272 	allocate_cores(5);
273 
274 	CU_ASSERT(spdk_reactors_init() == 0);
275 
276 	MOCK_SET(spdk_env_get_current_core, 0);
277 
278 	spdk_for_each_reactor(for_each_reactor_cb, &count, &done, for_each_reactor_done);
279 
280 	MOCK_CLEAR(spdk_env_get_current_core);
281 
282 	/* We have not processed any event yet, so count and done should be 0 and false,
283 	 *  respectively.
284 	 */
285 	CU_ASSERT(count == 0);
286 
287 	/* Poll each reactor to verify the event is passed to each */
288 	for (i = 0; i < 5; i++) {
289 		reactor = spdk_reactor_get(i);
290 		CU_ASSERT(reactor != NULL);
291 
292 		event_queue_run_batch(reactor);
293 		CU_ASSERT(count == (i + 1));
294 		CU_ASSERT(done == false);
295 	}
296 
297 	/* After each reactor is called, the completion calls it one more time. */
298 	reactor = spdk_reactor_get(0);
299 	CU_ASSERT(reactor != NULL);
300 
301 	event_queue_run_batch(reactor);
302 	CU_ASSERT(count == 6);
303 	CU_ASSERT(done == true);
304 
305 	spdk_reactors_fini();
306 
307 	free_cores();
308 }
309 
310 static int
311 poller_run_idle(void *ctx)
312 {
313 	uint64_t delay_us = (uint64_t)ctx;
314 
315 	spdk_delay_us(delay_us);
316 
317 	return 0;
318 }
319 
320 static int
321 poller_run_busy(void *ctx)
322 {
323 	uint64_t delay_us = (uint64_t)ctx;
324 
325 	spdk_delay_us(delay_us);
326 
327 	return 1;
328 }
329 
330 static void
331 test_reactor_stats(void)
332 {
333 	struct spdk_cpuset cpuset = {};
334 	struct spdk_thread *thread1, *thread2;
335 	struct spdk_reactor *reactor;
336 	struct spdk_poller *busy1, *idle1, *busy2, *idle2;
337 	int rc __attribute__((unused));
338 
339 	/* Test case is the following:
340 	 * Create a reactor on CPU core0.
341 	 * Create thread1 and thread2 simultaneously on reactor0 at TSC = 100.
342 	 * Reactor runs
343 	 * - thread1 for 100 with busy
344 	 * - thread2 for 200 with idle
345 	 * - thread1 for 300 with idle
346 	 * - thread2 for 400 with busy.
347 	 * Then,
348 	 * - both elapsed TSC of thread1 and thread2 should be 1000 (= 100 + 900).
349 	 * - busy TSC of reactor should be 500 (= 100 + 400).
350 	 * - idle TSC of reactor should be 500 (= 200 + 300).
351 	 */
352 
353 	allocate_cores(1);
354 
355 	CU_ASSERT(spdk_reactors_init() == 0);
356 
357 	spdk_cpuset_set_cpu(&cpuset, 0, true);
358 
359 	MOCK_SET(spdk_env_get_current_core, 0);
360 	MOCK_SET(spdk_get_ticks, 100);
361 
362 	thread1 = spdk_thread_create(NULL, &cpuset);
363 	SPDK_CU_ASSERT_FATAL(thread1 != NULL);
364 
365 	thread2 = spdk_thread_create(NULL, &cpuset);
366 	SPDK_CU_ASSERT_FATAL(thread2 != NULL);
367 
368 	reactor = spdk_reactor_get(0);
369 	SPDK_CU_ASSERT_FATAL(reactor != NULL);
370 
371 	reactor->tsc_last = 100;
372 
373 	spdk_set_thread(thread1);
374 	busy1 = spdk_poller_register(poller_run_busy, (void *)100, 0);
375 	CU_ASSERT(busy1 != NULL);
376 
377 	spdk_set_thread(thread2);
378 	idle2 = spdk_poller_register(poller_run_idle, (void *)300, 0);
379 	CU_ASSERT(idle2 != NULL);
380 
381 	_reactor_run(reactor);
382 
383 	CU_ASSERT(thread1->tsc_last == 200);
384 	CU_ASSERT(thread1->stats.busy_tsc == 100);
385 	CU_ASSERT(thread1->stats.idle_tsc == 0);
386 	CU_ASSERT(thread2->tsc_last == 500);
387 	CU_ASSERT(thread2->stats.busy_tsc == 0);
388 	CU_ASSERT(thread2->stats.idle_tsc == 300);
389 
390 	CU_ASSERT(reactor->busy_tsc == 100);
391 	CU_ASSERT(reactor->idle_tsc == 300);
392 
393 	spdk_set_thread(thread1);
394 	spdk_poller_unregister(&busy1);
395 	idle1 = spdk_poller_register(poller_run_idle, (void *)200, 0);
396 	CU_ASSERT(idle1 != NULL);
397 
398 	spdk_set_thread(thread2);
399 	spdk_poller_unregister(&idle2);
400 	busy2 = spdk_poller_register(poller_run_busy, (void *)400, 0);
401 	CU_ASSERT(busy2 != NULL);
402 
403 	_reactor_run(reactor);
404 
405 	CU_ASSERT(thread1->tsc_last == 700);
406 	CU_ASSERT(thread1->stats.busy_tsc == 100);
407 	CU_ASSERT(thread1->stats.idle_tsc == 200);
408 	CU_ASSERT(thread2->tsc_last == 1100);
409 	CU_ASSERT(thread2->stats.busy_tsc == 400);
410 	CU_ASSERT(thread2->stats.idle_tsc == 300);
411 
412 	CU_ASSERT(reactor->busy_tsc == 500);
413 	CU_ASSERT(reactor->idle_tsc == 500);
414 
415 	spdk_set_thread(thread1);
416 	spdk_poller_unregister(&idle1);
417 	spdk_thread_exit(thread1);
418 
419 	spdk_set_thread(thread2);
420 	spdk_poller_unregister(&busy2);
421 	spdk_thread_exit(thread2);
422 
423 	_reactor_run(reactor);
424 
425 	CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
426 
427 	spdk_reactors_fini();
428 
429 	free_cores();
430 }
431 
432 int
433 main(int argc, char **argv)
434 {
435 	CU_pSuite suite = NULL;
436 	unsigned int num_failures;
437 
438 	CU_set_error_action(CUEA_ABORT);
439 	CU_initialize_registry();
440 
441 	suite = CU_add_suite("app_suite", NULL, NULL);
442 
443 	CU_ADD_TEST(suite, test_create_reactor);
444 	CU_ADD_TEST(suite, test_init_reactors);
445 	CU_ADD_TEST(suite, test_event_call);
446 	CU_ADD_TEST(suite, test_schedule_thread);
447 	CU_ADD_TEST(suite, test_reschedule_thread);
448 	CU_ADD_TEST(suite, test_for_each_reactor);
449 	CU_ADD_TEST(suite, test_reactor_stats);
450 
451 	CU_basic_set_mode(CU_BRM_VERBOSE);
452 	CU_basic_run_tests();
453 	num_failures = CU_get_number_of_failures();
454 	CU_cleanup_registry();
455 
456 	return num_failures;
457 }
458