1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2017 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "spdk_internal/cunit.h"
7 #include "spdk/thread.h"
8 #include "spdk_internal/mock.h"
9
10 #include "common/lib/test_env.c"
11
12 static uint32_t g_ut_num_threads;
13
14 int allocate_threads(int num_threads);
15 void free_threads(void);
16 void poll_threads(void);
17 bool poll_thread(uintptr_t thread_id);
18 bool poll_thread_times(uintptr_t thread_id, uint32_t max_polls);
19
20 struct ut_msg {
21 spdk_msg_fn fn;
22 void *ctx;
23 TAILQ_ENTRY(ut_msg) link;
24 };
25
26 struct ut_thread {
27 struct spdk_thread *thread;
28 struct spdk_io_channel *ch;
29 };
30
31 struct ut_thread *g_ut_threads;
32
33 #define INVALID_THREAD 0x1000
34
35 static uint64_t g_ut_thread_id = INVALID_THREAD;
36
37 static void
set_thread(uintptr_t thread_id)38 set_thread(uintptr_t thread_id)
39 {
40 g_ut_thread_id = thread_id;
41 if (thread_id == INVALID_THREAD) {
42 spdk_set_thread(NULL);
43 } else {
44 spdk_set_thread(g_ut_threads[thread_id].thread);
45 }
46
47 }
48
49 int
allocate_threads(int num_threads)50 allocate_threads(int num_threads)
51 {
52 struct spdk_thread *thread;
53 uint32_t i;
54
55 spdk_thread_lib_init(NULL, 0);
56
57 g_ut_num_threads = num_threads;
58
59 g_ut_threads = calloc(num_threads, sizeof(*g_ut_threads));
60 assert(g_ut_threads != NULL);
61
62 for (i = 0; i < g_ut_num_threads; i++) {
63 set_thread(i);
64 thread = spdk_thread_create(NULL, NULL);
65 assert(thread != NULL);
66 g_ut_threads[i].thread = thread;
67 }
68
69 set_thread(INVALID_THREAD);
70 return 0;
71 }
72
73 void
free_threads(void)74 free_threads(void)
75 {
76 uint32_t i, num_threads;
77 struct spdk_thread *thread;
78
79 for (i = 0; i < g_ut_num_threads; i++) {
80 set_thread(i);
81 thread = g_ut_threads[i].thread;
82 spdk_thread_exit(thread);
83 }
84
85 num_threads = g_ut_num_threads;
86
87 while (num_threads != 0) {
88 for (i = 0; i < g_ut_num_threads; i++) {
89 set_thread(i);
90 thread = g_ut_threads[i].thread;
91 if (thread == NULL) {
92 continue;
93 }
94
95 if (spdk_thread_is_exited(thread)) {
96 g_ut_threads[i].thread = NULL;
97 num_threads--;
98 spdk_thread_destroy(thread);
99 } else {
100 spdk_thread_poll(thread, 0, 0);
101 }
102 }
103 }
104
105 g_ut_num_threads = 0;
106 free(g_ut_threads);
107 g_ut_threads = NULL;
108
109 spdk_thread_lib_fini();
110 }
111
112 bool
poll_thread_times(uintptr_t thread_id,uint32_t max_polls)113 poll_thread_times(uintptr_t thread_id, uint32_t max_polls)
114 {
115 bool busy = false;
116 struct ut_thread *thread = &g_ut_threads[thread_id];
117 uintptr_t original_thread_id;
118 uint32_t polls_executed = 0;
119 uint64_t now;
120
121 if (max_polls == 0) {
122 /* If max_polls is set to 0,
123 * poll until no operation is pending. */
124 return poll_thread(thread_id);
125 }
126 assert(thread_id != (uintptr_t)INVALID_THREAD);
127 assert(thread_id < g_ut_num_threads);
128
129 original_thread_id = g_ut_thread_id;
130 set_thread(INVALID_THREAD);
131
132 now = spdk_get_ticks();
133 while (polls_executed < max_polls) {
134 if (spdk_thread_poll(thread->thread, 1, now) > 0) {
135 busy = true;
136 }
137 now = spdk_thread_get_last_tsc(thread->thread);
138 polls_executed++;
139 }
140
141 set_thread(original_thread_id);
142
143 return busy;
144 }
145
146 bool
poll_thread(uintptr_t thread_id)147 poll_thread(uintptr_t thread_id)
148 {
149 bool busy = false;
150 struct ut_thread *thread = &g_ut_threads[thread_id];
151 uintptr_t original_thread_id;
152 uint64_t now;
153
154 assert(thread_id != (uintptr_t)INVALID_THREAD);
155 assert(thread_id < g_ut_num_threads);
156
157 original_thread_id = g_ut_thread_id;
158 set_thread(INVALID_THREAD);
159
160 now = spdk_get_ticks();
161 while (spdk_thread_poll(thread->thread, 0, now) > 0) {
162 now = spdk_thread_get_last_tsc(thread->thread);
163 busy = true;
164 }
165
166 set_thread(original_thread_id);
167
168 return busy;
169 }
170
171 void
poll_threads(void)172 poll_threads(void)
173 {
174 while (true) {
175 bool busy = false;
176
177 for (uint32_t i = 0; i < g_ut_num_threads; i++) {
178 busy = busy || poll_thread(i);
179 }
180
181 if (!busy) {
182 break;
183 }
184 }
185 }
186