1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 */ 4 5 #include "spdk/stdinc.h" 6 7 #include "spdk_cunit.h" 8 #include "common/lib/test_env.c" 9 #include "blob/bdev/blob_bdev.c" 10 11 DEFINE_STUB(spdk_bdev_io_type_supported, bool, (struct spdk_bdev *bdev, 12 enum spdk_bdev_io_type io_type), false); 13 DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *g_bdev_io)); 14 DEFINE_STUB(spdk_bdev_queue_io_wait, int, 15 (struct spdk_bdev *bdev, struct spdk_io_channel *ch, 16 struct spdk_bdev_io_wait_entry *entry), 0); 17 DEFINE_STUB(spdk_bdev_read_blocks, int, 18 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, void *buf, 19 uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 20 void *cb_arg), 0); 21 DEFINE_STUB(spdk_bdev_write_blocks, int, 22 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, void *buf, 23 uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 24 void *cb_arg), 0); 25 DEFINE_STUB(spdk_bdev_readv_blocks, int, 26 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, 27 uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 28 void *cb_arg), 0); 29 DEFINE_STUB(spdk_bdev_writev_blocks, int, 30 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, 31 uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 32 void *cb_arg), 0); 33 DEFINE_STUB(spdk_bdev_readv_blocks_ext, int, 34 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, 35 uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 36 void *cb_arg, struct spdk_bdev_ext_io_opts *opts), 0); 37 DEFINE_STUB(spdk_bdev_writev_blocks_ext, int, 38 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, 39 uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 40 void *cb_arg, struct spdk_bdev_ext_io_opts *opts), 0); 41 DEFINE_STUB(spdk_bdev_write_zeroes_blocks, int, 42 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks, 43 uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg), 0); 44 DEFINE_STUB(spdk_bdev_unmap_blocks, int, 45 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks, 46 uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg), 0); 47 DEFINE_STUB(spdk_bdev_copy_blocks, int, 48 (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t dst_offset_blocks, 49 uint64_t src_offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, 50 void *cb_arg), 0); 51 DEFINE_STUB(spdk_bdev_get_io_channel, struct spdk_io_channel *, 52 (struct spdk_bdev_desc *desc), NULL); 53 54 struct spdk_bdev { 55 char name[16]; 56 uint64_t blockcnt; 57 uint32_t blocklen; 58 uint32_t open_cnt; 59 enum spdk_bdev_claim_type claim_type; 60 struct spdk_bdev_module *claim_module; 61 struct spdk_bdev_desc *claim_desc; 62 }; 63 64 struct spdk_bdev_desc { 65 struct spdk_bdev *bdev; 66 bool write; 67 enum spdk_bdev_claim_type claim_type; 68 }; 69 70 struct spdk_bdev *g_bdev; 71 72 static struct spdk_bdev_module g_bdev_mod = { 73 .name = "blob_bdev_ut" 74 }; 75 76 static struct spdk_bdev * 77 get_bdev(const char *bdev_name) 78 { 79 if (g_bdev == NULL) { 80 return NULL; 81 } 82 83 if (strcmp(bdev_name, g_bdev->name) != 0) { 84 return NULL; 85 } 86 87 return g_bdev; 88 } 89 90 int 91 spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event_cb, 92 void *event_ctx, struct spdk_bdev_desc **_desc) 93 { 94 struct spdk_bdev_desc *desc; 95 struct spdk_bdev *bdev = get_bdev(bdev_name); 96 97 if (bdev == NULL) { 98 return -ENODEV; 99 } 100 101 if (write && bdev->claim_module != NULL) { 102 return -EPERM; 103 } 104 105 desc = calloc(1, sizeof(*desc)); 106 desc->bdev = g_bdev; 107 desc->write = write; 108 *_desc = desc; 109 bdev->open_cnt++; 110 111 return 0; 112 } 113 114 void 115 spdk_bdev_close(struct spdk_bdev_desc *desc) 116 { 117 struct spdk_bdev *bdev = desc->bdev; 118 119 bdev->open_cnt--; 120 if (bdev->claim_desc == desc) { 121 bdev->claim_desc = NULL; 122 bdev->claim_type = SPDK_BDEV_CLAIM_NONE; 123 bdev->claim_module = NULL; 124 } 125 free(desc); 126 } 127 128 struct spdk_bdev * 129 spdk_bdev_desc_get_bdev(struct spdk_bdev_desc *desc) 130 { 131 return desc->bdev; 132 } 133 134 uint64_t 135 spdk_bdev_get_num_blocks(const struct spdk_bdev *bdev) 136 { 137 return bdev->blockcnt; 138 } 139 140 uint32_t 141 spdk_bdev_get_block_size(const struct spdk_bdev *bdev) 142 { 143 return bdev->blocklen; 144 } 145 146 /* This is a simple approximation: it does not support shared claims */ 147 int 148 spdk_bdev_module_claim_bdev_desc(struct spdk_bdev_desc *desc, enum spdk_bdev_claim_type type, 149 struct spdk_bdev_claim_opts *opts, 150 struct spdk_bdev_module *module) 151 { 152 struct spdk_bdev *bdev = desc->bdev; 153 154 if (bdev->claim_module != NULL) { 155 return -EPERM; 156 } 157 158 bdev->claim_type = type; 159 bdev->claim_module = module; 160 bdev->claim_desc = desc; 161 162 desc->claim_type = type; 163 164 return 0; 165 } 166 167 static void 168 init_bdev(struct spdk_bdev *bdev, const char *name, uint64_t num_blocks) 169 { 170 memset(bdev, 0, sizeof(*bdev)); 171 snprintf(bdev->name, sizeof(bdev->name), "%s", name); 172 bdev->blockcnt = num_blocks; 173 } 174 175 static void 176 create_bs_dev(void) 177 { 178 struct spdk_bdev bdev; 179 struct spdk_bs_dev *bs_dev = NULL; 180 struct blob_bdev *blob_bdev; 181 int rc; 182 183 init_bdev(&bdev, "bdev0", 16); 184 g_bdev = &bdev; 185 186 rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev); 187 CU_ASSERT(rc == 0); 188 SPDK_CU_ASSERT_FATAL(bs_dev != NULL); 189 CU_ASSERT(bdev.open_cnt == 1); 190 191 blob_bdev = (struct blob_bdev *)bs_dev; 192 CU_ASSERT(blob_bdev->desc != NULL); 193 CU_ASSERT(blob_bdev->desc->write); 194 CU_ASSERT(blob_bdev->desc->bdev == g_bdev); 195 CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE); 196 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 197 198 bs_dev->destroy(bs_dev); 199 CU_ASSERT(bdev.open_cnt == 0); 200 g_bdev = NULL; 201 } 202 203 static void 204 create_bs_dev_ro(void) 205 { 206 struct spdk_bdev bdev; 207 struct spdk_bs_dev *bs_dev = NULL; 208 struct blob_bdev *blob_bdev; 209 struct spdk_bdev_bs_dev_opts opts = { 0 }; 210 int rc; 211 212 /* opts with the wrong size returns -EINVAL */ 213 rc = spdk_bdev_create_bs_dev("nope", false, &opts, sizeof(opts) + 8, NULL, NULL, &bs_dev); 214 CU_ASSERT(rc == -EINVAL); 215 216 /* opts with the right size is OK, but can still fail if the device doesn't exist. */ 217 opts.opts_size = sizeof(opts); 218 rc = spdk_bdev_create_bs_dev("nope", false, &opts, sizeof(opts), NULL, NULL, &bs_dev); 219 CU_ASSERT(rc == -ENODEV); 220 221 init_bdev(&bdev, "bdev0", 16); 222 g_bdev = &bdev; 223 224 /* The normal way to create a read-only device */ 225 rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev); 226 CU_ASSERT(rc == 0); 227 SPDK_CU_ASSERT_FATAL(bs_dev != NULL); 228 CU_ASSERT(bdev.open_cnt == 1); 229 230 blob_bdev = (struct blob_bdev *)bs_dev; 231 CU_ASSERT(blob_bdev->desc != NULL); 232 CU_ASSERT(!blob_bdev->desc->write); 233 CU_ASSERT(blob_bdev->desc->bdev == g_bdev); 234 CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE); 235 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 236 237 bs_dev->destroy(bs_dev); 238 CU_ASSERT(bdev.open_cnt == 0); 239 g_bdev = NULL; 240 } 241 242 static void 243 create_bs_dev_rw(void) 244 { 245 struct spdk_bdev bdev; 246 struct spdk_bs_dev *bs_dev = NULL; 247 struct blob_bdev *blob_bdev; 248 int rc; 249 250 init_bdev(&bdev, "bdev0", 16); 251 g_bdev = &bdev; 252 253 /* This is equivalent to spdk_bdev_create_bs_dev_ext() */ 254 rc = spdk_bdev_create_bs_dev("bdev0", true, NULL, 0, NULL, NULL, &bs_dev); 255 CU_ASSERT(rc == 0); 256 SPDK_CU_ASSERT_FATAL(bs_dev != NULL); 257 CU_ASSERT(bdev.open_cnt == 1); 258 259 blob_bdev = (struct blob_bdev *)bs_dev; 260 CU_ASSERT(blob_bdev->desc != NULL); 261 CU_ASSERT(blob_bdev->desc->write); 262 CU_ASSERT(blob_bdev->desc->bdev == g_bdev); 263 CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE); 264 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 265 266 bs_dev->destroy(bs_dev); 267 CU_ASSERT(bdev.open_cnt == 0); 268 g_bdev = NULL; 269 } 270 271 static void 272 claim_bs_dev(void) 273 { 274 struct spdk_bdev bdev; 275 struct spdk_bs_dev *bs_dev = NULL, *bs_dev2 = NULL; 276 struct blob_bdev *blob_bdev; 277 int rc; 278 279 init_bdev(&bdev, "bdev0", 16); 280 g_bdev = &bdev; 281 282 rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev); 283 CU_ASSERT(rc == 0); 284 SPDK_CU_ASSERT_FATAL(bs_dev != NULL); 285 286 blob_bdev = (struct blob_bdev *)bs_dev; 287 CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE); 288 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 289 CU_ASSERT(blob_bdev->desc->write); 290 291 /* Can get an exclusive write claim */ 292 rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod); 293 CU_ASSERT(rc == 0); 294 CU_ASSERT(blob_bdev->desc->write); 295 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE); 296 CU_ASSERT(bdev.claim_desc == blob_bdev->desc); 297 298 /* Claim blocks a second writer without messing up the first one. */ 299 rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev2); 300 CU_ASSERT(rc == -EPERM); 301 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE); 302 CU_ASSERT(bdev.claim_desc == blob_bdev->desc); 303 304 /* Claim blocks a second claim without messing up the first one. */ 305 rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod); 306 CU_ASSERT(rc == -EPERM); 307 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE); 308 CU_ASSERT(bdev.claim_desc == blob_bdev->desc); 309 310 bs_dev->destroy(bs_dev); 311 CU_ASSERT(bdev.open_cnt == 0); 312 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 313 CU_ASSERT(bdev.claim_module == NULL); 314 CU_ASSERT(bdev.claim_desc == NULL); 315 g_bdev = NULL; 316 } 317 318 static void 319 claim_bs_dev_ro(void) 320 { 321 struct spdk_bdev bdev; 322 struct spdk_bs_dev *bs_dev = NULL, *bs_dev2 = NULL; 323 struct blob_bdev *blob_bdev; 324 int rc; 325 326 init_bdev(&bdev, "bdev0", 16); 327 g_bdev = &bdev; 328 329 rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev); 330 CU_ASSERT(rc == 0); 331 SPDK_CU_ASSERT_FATAL(bs_dev != NULL); 332 333 blob_bdev = (struct blob_bdev *)bs_dev; 334 CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE); 335 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 336 CU_ASSERT(!blob_bdev->desc->write); 337 338 /* Can get an shared reader claim */ 339 rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod); 340 CU_ASSERT(rc == 0); 341 CU_ASSERT(!blob_bdev->desc->write); 342 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_NONE); 343 CU_ASSERT(bdev.claim_desc == blob_bdev->desc); 344 345 /* Claim blocks a writer without messing up the claim. */ 346 rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev2); 347 CU_ASSERT(rc == -EPERM); 348 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_NONE); 349 CU_ASSERT(bdev.claim_desc == blob_bdev->desc); 350 351 /* Another reader is just fine */ 352 rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev2); 353 CU_ASSERT(rc == 0); 354 SPDK_CU_ASSERT_FATAL(bs_dev2 != NULL); 355 bs_dev2->destroy(bs_dev2); 356 357 bs_dev->destroy(bs_dev); 358 CU_ASSERT(bdev.open_cnt == 0); 359 CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE); 360 CU_ASSERT(bdev.claim_module == NULL); 361 CU_ASSERT(bdev.claim_desc == NULL); 362 g_bdev = NULL; 363 } 364 365 int 366 main(int argc, char **argv) 367 { 368 CU_pSuite suite; 369 unsigned int num_failures; 370 371 CU_set_error_action(CUEA_ABORT); 372 CU_initialize_registry(); 373 374 suite = CU_add_suite("blob_bdev", NULL, NULL); 375 376 CU_ADD_TEST(suite, create_bs_dev); 377 CU_ADD_TEST(suite, create_bs_dev_ro); 378 CU_ADD_TEST(suite, create_bs_dev_rw); 379 CU_ADD_TEST(suite, claim_bs_dev); 380 CU_ADD_TEST(suite, claim_bs_dev_ro); 381 382 CU_basic_set_mode(CU_BRM_VERBOSE); 383 CU_basic_run_tests(); 384 num_failures = CU_get_number_of_failures(); 385 CU_cleanup_registry(); 386 387 return num_failures; 388 } 389