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 36 #include "lib/test_env.c" 37 #include "lib/ut_multithread.c" 38 39 /* HACK: disable VTune integration so the unit test doesn't need VTune headers and libs to build */ 40 #undef SPDK_CONFIG_VTUNE 41 42 #include "bdev.c" 43 44 #define BDEV_UT_NUM_THREADS 3 45 46 DEFINE_STUB_V(spdk_scsi_nvme_translate, (const struct spdk_bdev_io *bdev_io, 47 int *sc, int *sk, int *asc, int *ascq)); 48 49 struct ut_bdev { 50 struct spdk_bdev bdev; 51 int io_target; 52 }; 53 54 struct ut_bdev_channel { 55 TAILQ_HEAD(, spdk_bdev_io) outstanding_io; 56 }; 57 58 struct ut_bdev g_bdev; 59 struct spdk_bdev_desc *g_desc; 60 61 static int 62 stub_create_ch(void *io_device, void *ctx_buf) 63 { 64 struct ut_bdev_channel *ch = ctx_buf; 65 66 TAILQ_INIT(&ch->outstanding_io); 67 return 0; 68 } 69 70 static void 71 stub_destroy_ch(void *io_device, void *ctx_buf) 72 { 73 } 74 75 static struct spdk_io_channel * 76 stub_get_io_channel(void *ctx) 77 { 78 return spdk_get_io_channel(&g_bdev.io_target); 79 } 80 81 static int 82 stub_destruct(void *ctx) 83 { 84 return 0; 85 } 86 87 static void 88 stub_submit_request(struct spdk_io_channel *_ch, struct spdk_bdev_io *bdev_io) 89 { 90 struct ut_bdev_channel *ch = spdk_io_channel_get_ctx(_ch); 91 92 TAILQ_INSERT_TAIL(&ch->outstanding_io, bdev_io, module_link); 93 } 94 95 static void 96 stub_complete_io(void) 97 { 98 struct spdk_io_channel *_ch = spdk_get_io_channel(&g_bdev.io_target); 99 struct ut_bdev_channel *ch = spdk_io_channel_get_ctx(_ch); 100 struct spdk_bdev_io *io; 101 102 while (!TAILQ_EMPTY(&ch->outstanding_io)) { 103 io = TAILQ_FIRST(&ch->outstanding_io); 104 TAILQ_REMOVE(&ch->outstanding_io, io, module_link); 105 spdk_bdev_io_complete(io, SPDK_BDEV_IO_STATUS_SUCCESS); 106 } 107 108 spdk_put_io_channel(_ch); 109 } 110 111 static struct spdk_bdev_fn_table fn_table = { 112 .get_io_channel = stub_get_io_channel, 113 .destruct = stub_destruct, 114 .submit_request = stub_submit_request, 115 }; 116 117 static int 118 module_init(void) 119 { 120 return 0; 121 } 122 123 static void 124 module_fini(void) 125 { 126 } 127 128 SPDK_BDEV_MODULE_REGISTER(bdev_ut, module_init, module_fini, NULL, NULL, NULL) 129 130 static void 131 register_bdev(void) 132 { 133 g_bdev.bdev.name = "bdev_ut"; 134 g_bdev.bdev.fn_table = &fn_table; 135 g_bdev.bdev.module = SPDK_GET_BDEV_MODULE(bdev_ut); 136 137 spdk_io_device_register(&g_bdev.io_target, stub_create_ch, stub_destroy_ch, 138 sizeof(struct ut_bdev_channel)); 139 spdk_bdev_register(&g_bdev.bdev); 140 } 141 142 static void 143 unregister_bdev(void) 144 { 145 /* Handle any deferred messages. */ 146 poll_threads(); 147 spdk_bdev_unregister(&g_bdev.bdev); 148 spdk_io_device_unregister(&g_bdev.io_target, NULL); 149 memset(&g_bdev, 0, sizeof(g_bdev)); 150 } 151 152 static void 153 bdev_init_cb(void *done, int rc) 154 { 155 CU_ASSERT(rc == 0); 156 *(bool *)done = true; 157 } 158 159 static void 160 setup_test(void) 161 { 162 bool done = false; 163 164 allocate_threads(BDEV_UT_NUM_THREADS); 165 spdk_bdev_initialize(bdev_init_cb, &done, NULL, NULL); 166 register_bdev(); 167 spdk_bdev_open(&g_bdev.bdev, true, NULL, NULL, &g_desc); 168 } 169 170 static void 171 teardown_test(void) 172 { 173 spdk_bdev_close(g_desc); 174 g_desc = NULL; 175 unregister_bdev(); 176 spdk_bdev_finish(); 177 free_threads(); 178 } 179 180 static void 181 basic(void) 182 { 183 setup_test(); 184 185 set_thread(0); 186 187 g_ut_threads[0].ch = spdk_bdev_get_io_channel(g_desc); 188 spdk_put_io_channel(g_ut_threads[0].ch); 189 190 teardown_test(); 191 } 192 193 static void 194 reset_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 195 { 196 bool *done = cb_arg; 197 198 CU_ASSERT(success == true); 199 *done = true; 200 spdk_bdev_free_io(bdev_io); 201 } 202 203 static void 204 put_channel_during_reset(void) 205 { 206 struct spdk_io_channel *io_ch; 207 bool done = false; 208 209 setup_test(); 210 211 set_thread(0); 212 io_ch = spdk_bdev_get_io_channel(g_desc); 213 CU_ASSERT(io_ch != NULL); 214 215 /* 216 * Start a reset, but then put the I/O channel before 217 * the deferred messages for the reset get a chance to 218 * execute. 219 */ 220 spdk_bdev_reset(g_desc, io_ch, reset_done, &done); 221 spdk_put_io_channel(io_ch); 222 poll_threads(); 223 stub_complete_io(); 224 225 teardown_test(); 226 } 227 228 static void 229 aborted_reset_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 230 { 231 enum spdk_bdev_io_status *status = cb_arg; 232 233 *status = success ? SPDK_BDEV_IO_STATUS_SUCCESS : SPDK_BDEV_IO_STATUS_FAILED; 234 spdk_bdev_free_io(bdev_io); 235 } 236 237 static void 238 aborted_reset(void) 239 { 240 struct spdk_io_channel *io_ch[2]; 241 enum spdk_bdev_io_status status1, status2; 242 243 setup_test(); 244 245 set_thread(0); 246 io_ch[0] = spdk_bdev_get_io_channel(g_desc); 247 CU_ASSERT(io_ch[0] != NULL); 248 spdk_bdev_reset(g_desc, io_ch[0], aborted_reset_done, &status1); 249 poll_threads(); 250 CU_ASSERT(g_bdev.bdev.reset_in_progress != NULL); 251 252 /* 253 * First reset has been submitted on ch0. Now submit a second 254 * reset on ch1 which will get queued since there is already a 255 * reset in progress. 256 */ 257 set_thread(1); 258 io_ch[1] = spdk_bdev_get_io_channel(g_desc); 259 CU_ASSERT(io_ch[1] != NULL); 260 spdk_bdev_reset(g_desc, io_ch[1], aborted_reset_done, &status2); 261 poll_threads(); 262 CU_ASSERT(g_bdev.bdev.reset_in_progress != NULL); 263 264 /* 265 * Now destroy ch1. This will abort the queued reset. Check that 266 * the second reset was completed with failed status. Also check 267 * that bdev->reset_in_progress != NULL, since the original reset 268 * has not been completed yet. This ensures that the bdev code is 269 * correctly noticing that the failed reset is *not* the one that 270 * had been submitted to the bdev module. 271 */ 272 set_thread(1); 273 spdk_put_io_channel(io_ch[1]); 274 poll_threads(); 275 CU_ASSERT(status2 == SPDK_BDEV_IO_STATUS_FAILED); 276 CU_ASSERT(g_bdev.bdev.reset_in_progress != NULL); 277 278 /* 279 * Now complete the first reset, verify that it completed with SUCCESS 280 * status and that bdev->reset_in_progress is also set back to NULL. 281 */ 282 set_thread(0); 283 spdk_put_io_channel(io_ch[0]); 284 stub_complete_io(); 285 poll_threads(); 286 CU_ASSERT(status1 == SPDK_BDEV_IO_STATUS_SUCCESS); 287 CU_ASSERT(g_bdev.bdev.reset_in_progress == NULL); 288 289 teardown_test(); 290 } 291 292 int 293 main(int argc, char **argv) 294 { 295 CU_pSuite suite = NULL; 296 unsigned int num_failures; 297 298 if (CU_initialize_registry() != CUE_SUCCESS) { 299 return CU_get_error(); 300 } 301 302 suite = CU_add_suite("bdev", NULL, NULL); 303 if (suite == NULL) { 304 CU_cleanup_registry(); 305 return CU_get_error(); 306 } 307 308 if ( 309 CU_add_test(suite, "basic", basic) == NULL || 310 CU_add_test(suite, "put_channel_during_reset", put_channel_during_reset) == NULL || 311 CU_add_test(suite, "aborted_reset", aborted_reset) == NULL 312 ) { 313 CU_cleanup_registry(); 314 return CU_get_error(); 315 } 316 317 CU_basic_set_mode(CU_BRM_VERBOSE); 318 CU_basic_run_tests(); 319 num_failures = CU_get_number_of_failures(); 320 CU_cleanup_registry(); 321 return num_failures; 322 } 323