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_cunit.h" 35 #include "spdk/thread.h" 36 #include "spdk_internal/mock.h" 37 #include "spdk_internal/thread.h" 38 39 #include "common/lib/test_env.c" 40 41 static uint32_t g_ut_num_threads; 42 43 int allocate_threads(int num_threads); 44 void free_threads(void); 45 void poll_threads(void); 46 bool poll_thread(uintptr_t thread_id); 47 bool poll_thread_times(uintptr_t thread_id, uint32_t max_polls); 48 49 struct ut_msg { 50 spdk_msg_fn fn; 51 void *ctx; 52 TAILQ_ENTRY(ut_msg) link; 53 }; 54 55 struct ut_thread { 56 struct spdk_thread *thread; 57 struct spdk_io_channel *ch; 58 }; 59 60 struct ut_thread *g_ut_threads; 61 62 #define INVALID_THREAD 0x1000 63 64 static uint64_t g_ut_thread_id = INVALID_THREAD; 65 66 static void 67 set_thread(uintptr_t thread_id) 68 { 69 g_ut_thread_id = thread_id; 70 if (thread_id == INVALID_THREAD) { 71 spdk_set_thread(NULL); 72 } else { 73 spdk_set_thread(g_ut_threads[thread_id].thread); 74 } 75 76 } 77 78 int 79 allocate_threads(int num_threads) 80 { 81 struct spdk_thread *thread; 82 uint32_t i; 83 84 spdk_thread_lib_init(NULL, 0); 85 86 g_ut_num_threads = num_threads; 87 88 g_ut_threads = calloc(num_threads, sizeof(*g_ut_threads)); 89 assert(g_ut_threads != NULL); 90 91 for (i = 0; i < g_ut_num_threads; i++) { 92 set_thread(i); 93 thread = spdk_thread_create(NULL, NULL); 94 assert(thread != NULL); 95 g_ut_threads[i].thread = thread; 96 } 97 98 set_thread(INVALID_THREAD); 99 return 0; 100 } 101 102 void 103 free_threads(void) 104 { 105 uint32_t i, num_threads; 106 struct spdk_thread *thread; 107 108 for (i = 0; i < g_ut_num_threads; i++) { 109 set_thread(i); 110 thread = g_ut_threads[i].thread; 111 spdk_thread_exit(thread); 112 } 113 114 num_threads = g_ut_num_threads; 115 116 while (num_threads != 0) { 117 for (i = 0; i < g_ut_num_threads; i++) { 118 set_thread(i); 119 thread = g_ut_threads[i].thread; 120 if (thread == NULL) { 121 continue; 122 } 123 124 if (spdk_thread_is_exited(thread)) { 125 g_ut_threads[i].thread = NULL; 126 num_threads--; 127 spdk_thread_destroy(thread); 128 } else { 129 spdk_thread_poll(thread, 0, 0); 130 } 131 } 132 } 133 134 g_ut_num_threads = 0; 135 free(g_ut_threads); 136 g_ut_threads = NULL; 137 138 spdk_thread_lib_fini(); 139 } 140 141 bool 142 poll_thread_times(uintptr_t thread_id, uint32_t max_polls) 143 { 144 bool busy = false; 145 struct ut_thread *thread = &g_ut_threads[thread_id]; 146 uintptr_t original_thread_id; 147 uint32_t polls_executed = 0; 148 uint64_t now; 149 150 if (max_polls == 0) { 151 /* If max_polls is set to 0, 152 * poll until no operation is pending. */ 153 return poll_thread(thread_id); 154 } 155 assert(thread_id != (uintptr_t)INVALID_THREAD); 156 assert(thread_id < g_ut_num_threads); 157 158 original_thread_id = g_ut_thread_id; 159 set_thread(INVALID_THREAD); 160 161 now = spdk_get_ticks(); 162 while (polls_executed < max_polls) { 163 if (spdk_thread_poll(thread->thread, 1, now) > 0) { 164 busy = true; 165 } 166 now = spdk_thread_get_last_tsc(thread->thread); 167 polls_executed++; 168 } 169 170 set_thread(original_thread_id); 171 172 return busy; 173 } 174 175 bool 176 poll_thread(uintptr_t thread_id) 177 { 178 bool busy = false; 179 struct ut_thread *thread = &g_ut_threads[thread_id]; 180 uintptr_t original_thread_id; 181 uint64_t now; 182 183 assert(thread_id != (uintptr_t)INVALID_THREAD); 184 assert(thread_id < g_ut_num_threads); 185 186 original_thread_id = g_ut_thread_id; 187 set_thread(INVALID_THREAD); 188 189 now = spdk_get_ticks(); 190 while (spdk_thread_poll(thread->thread, 0, now) > 0) { 191 now = spdk_thread_get_last_tsc(thread->thread); 192 busy = true; 193 } 194 195 set_thread(original_thread_id); 196 197 return busy; 198 } 199 200 void 201 poll_threads(void) 202 { 203 while (true) { 204 bool busy = false; 205 206 for (uint32_t i = 0; i < g_ut_num_threads; i++) { 207 busy = busy || poll_thread(i); 208 } 209 210 if (!busy) { 211 break; 212 } 213 } 214 } 215