1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2017 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/bdev.h" 9 #include "spdk/env.h" 10 #include "spdk/event.h" 11 #include "spdk/blob_bdev.h" 12 #include "spdk/blob.h" 13 #include "spdk/log.h" 14 #include "spdk/string.h" 15 16 /* 17 * We'll use this struct to gather housekeeping hello_context to pass between 18 * our events and callbacks. 19 */ 20 struct hello_context_t { 21 struct spdk_blob_store *bs; 22 struct spdk_blob *blob; 23 spdk_blob_id blobid; 24 struct spdk_io_channel *channel; 25 uint8_t *read_buff; 26 uint8_t *write_buff; 27 uint64_t io_unit_size; 28 int rc; 29 }; 30 31 /* 32 * Free up memory that we allocated. 33 */ 34 static void 35 hello_cleanup(struct hello_context_t *hello_context) 36 { 37 spdk_free(hello_context->read_buff); 38 spdk_free(hello_context->write_buff); 39 free(hello_context); 40 } 41 42 /* 43 * Callback routine for the blobstore unload. 44 */ 45 static void 46 unload_complete(void *cb_arg, int bserrno) 47 { 48 struct hello_context_t *hello_context = cb_arg; 49 50 SPDK_NOTICELOG("entry\n"); 51 if (bserrno) { 52 SPDK_ERRLOG("Error %d unloading the bobstore\n", bserrno); 53 hello_context->rc = bserrno; 54 } 55 56 spdk_app_stop(hello_context->rc); 57 } 58 59 /* 60 * Unload the blobstore, cleaning up as needed. 61 */ 62 static void 63 unload_bs(struct hello_context_t *hello_context, char *msg, int bserrno) 64 { 65 if (bserrno) { 66 SPDK_ERRLOG("%s (err %d)\n", msg, bserrno); 67 hello_context->rc = bserrno; 68 } 69 if (hello_context->bs) { 70 if (hello_context->channel) { 71 spdk_bs_free_io_channel(hello_context->channel); 72 } 73 spdk_bs_unload(hello_context->bs, unload_complete, hello_context); 74 } else { 75 spdk_app_stop(bserrno); 76 } 77 } 78 79 /* 80 * Callback routine for the deletion of a blob. 81 */ 82 static void 83 delete_complete(void *arg1, int bserrno) 84 { 85 struct hello_context_t *hello_context = arg1; 86 87 SPDK_NOTICELOG("entry\n"); 88 if (bserrno) { 89 unload_bs(hello_context, "Error in delete completion", 90 bserrno); 91 return; 92 } 93 94 /* We're all done, we can unload the blobstore. */ 95 unload_bs(hello_context, "", 0); 96 } 97 98 /* 99 * Function for deleting a blob. 100 */ 101 static void 102 delete_blob(void *arg1, int bserrno) 103 { 104 struct hello_context_t *hello_context = arg1; 105 106 SPDK_NOTICELOG("entry\n"); 107 if (bserrno) { 108 unload_bs(hello_context, "Error in close completion", 109 bserrno); 110 return; 111 } 112 113 spdk_bs_delete_blob(hello_context->bs, hello_context->blobid, 114 delete_complete, hello_context); 115 } 116 117 /* 118 * Callback function for reading a blob. 119 */ 120 static void 121 read_complete(void *arg1, int bserrno) 122 { 123 struct hello_context_t *hello_context = arg1; 124 int match_res = -1; 125 126 SPDK_NOTICELOG("entry\n"); 127 if (bserrno) { 128 unload_bs(hello_context, "Error in read completion", 129 bserrno); 130 return; 131 } 132 133 /* Now let's make sure things match. */ 134 match_res = memcmp(hello_context->write_buff, hello_context->read_buff, 135 hello_context->io_unit_size); 136 if (match_res) { 137 unload_bs(hello_context, "Error in data compare", -1); 138 return; 139 } else { 140 SPDK_NOTICELOG("read SUCCESS and data matches!\n"); 141 } 142 143 /* Now let's close it and delete the blob in the callback. */ 144 spdk_blob_close(hello_context->blob, delete_blob, hello_context); 145 } 146 147 /* 148 * Function for reading a blob. 149 */ 150 static void 151 read_blob(struct hello_context_t *hello_context) 152 { 153 SPDK_NOTICELOG("entry\n"); 154 155 hello_context->read_buff = spdk_malloc(hello_context->io_unit_size, 156 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, 157 SPDK_MALLOC_DMA); 158 if (hello_context->read_buff == NULL) { 159 unload_bs(hello_context, "Error in memory allocation", 160 -ENOMEM); 161 return; 162 } 163 164 /* Issue the read and compare the results in the callback. */ 165 spdk_blob_io_read(hello_context->blob, hello_context->channel, 166 hello_context->read_buff, 0, 1, read_complete, 167 hello_context); 168 } 169 170 /* 171 * Callback function for writing a blob. 172 */ 173 static void 174 write_complete(void *arg1, int bserrno) 175 { 176 struct hello_context_t *hello_context = arg1; 177 178 SPDK_NOTICELOG("entry\n"); 179 if (bserrno) { 180 unload_bs(hello_context, "Error in write completion", 181 bserrno); 182 return; 183 } 184 185 /* Now let's read back what we wrote and make sure it matches. */ 186 read_blob(hello_context); 187 } 188 189 /* 190 * Function for writing to a blob. 191 */ 192 static void 193 blob_write(struct hello_context_t *hello_context) 194 { 195 SPDK_NOTICELOG("entry\n"); 196 197 /* 198 * Buffers for data transfer need to be allocated via SPDK. We will 199 * transfer 1 io_unit of 4K aligned data at offset 0 in the blob. 200 */ 201 hello_context->write_buff = spdk_malloc(hello_context->io_unit_size, 202 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, 203 SPDK_MALLOC_DMA); 204 if (hello_context->write_buff == NULL) { 205 unload_bs(hello_context, "Error in allocating memory", 206 -ENOMEM); 207 return; 208 } 209 memset(hello_context->write_buff, 0x5a, hello_context->io_unit_size); 210 211 /* Now we have to allocate a channel. */ 212 hello_context->channel = spdk_bs_alloc_io_channel(hello_context->bs); 213 if (hello_context->channel == NULL) { 214 unload_bs(hello_context, "Error in allocating channel", 215 -ENOMEM); 216 return; 217 } 218 219 /* Let's perform the write, 1 io_unit at offset 0. */ 220 spdk_blob_io_write(hello_context->blob, hello_context->channel, 221 hello_context->write_buff, 222 0, 1, write_complete, hello_context); 223 } 224 225 /* 226 * Callback function for syncing metadata. 227 */ 228 static void 229 sync_complete(void *arg1, int bserrno) 230 { 231 struct hello_context_t *hello_context = arg1; 232 233 SPDK_NOTICELOG("entry\n"); 234 if (bserrno) { 235 unload_bs(hello_context, "Error in sync callback", 236 bserrno); 237 return; 238 } 239 240 /* Blob has been created & sized & MD sync'd, let's write to it. */ 241 blob_write(hello_context); 242 } 243 244 static void 245 resize_complete(void *cb_arg, int bserrno) 246 { 247 struct hello_context_t *hello_context = cb_arg; 248 uint64_t total = 0; 249 250 if (bserrno) { 251 unload_bs(hello_context, "Error in blob resize", bserrno); 252 return; 253 } 254 255 total = spdk_blob_get_num_clusters(hello_context->blob); 256 SPDK_NOTICELOG("resized blob now has USED clusters of %" PRIu64 "\n", 257 total); 258 259 /* 260 * Metadata is stored in volatile memory for performance 261 * reasons and therefore needs to be synchronized with 262 * non-volatile storage to make it persistent. This can be 263 * done manually, as shown here, or if not it will be done 264 * automatically when the blob is closed. It is always a 265 * good idea to sync after making metadata changes unless 266 * it has an unacceptable impact on application performance. 267 */ 268 spdk_blob_sync_md(hello_context->blob, sync_complete, hello_context); 269 } 270 271 /* 272 * Callback function for opening a blob. 273 */ 274 static void 275 open_complete(void *cb_arg, struct spdk_blob *blob, int bserrno) 276 { 277 struct hello_context_t *hello_context = cb_arg; 278 uint64_t free = 0; 279 280 SPDK_NOTICELOG("entry\n"); 281 if (bserrno) { 282 unload_bs(hello_context, "Error in open completion", 283 bserrno); 284 return; 285 } 286 287 288 hello_context->blob = blob; 289 free = spdk_bs_free_cluster_count(hello_context->bs); 290 SPDK_NOTICELOG("blobstore has FREE clusters of %" PRIu64 "\n", 291 free); 292 293 /* 294 * Before we can use our new blob, we have to resize it 295 * as the initial size is 0. For this example we'll use the 296 * full size of the blobstore but it would be expected that 297 * there'd usually be many blobs of various sizes. The resize 298 * unit is a cluster. 299 */ 300 spdk_blob_resize(hello_context->blob, free, resize_complete, hello_context); 301 } 302 303 /* 304 * Callback function for creating a blob. 305 */ 306 static void 307 blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno) 308 { 309 struct hello_context_t *hello_context = arg1; 310 311 SPDK_NOTICELOG("entry\n"); 312 if (bserrno) { 313 unload_bs(hello_context, "Error in blob create callback", 314 bserrno); 315 return; 316 } 317 318 hello_context->blobid = blobid; 319 SPDK_NOTICELOG("new blob id %" PRIu64 "\n", hello_context->blobid); 320 321 /* We have to open the blob before we can do things like resize. */ 322 spdk_bs_open_blob(hello_context->bs, hello_context->blobid, 323 open_complete, hello_context); 324 } 325 326 /* 327 * Function for creating a blob. 328 */ 329 static void 330 create_blob(struct hello_context_t *hello_context) 331 { 332 SPDK_NOTICELOG("entry\n"); 333 spdk_bs_create_blob(hello_context->bs, blob_create_complete, hello_context); 334 } 335 336 /* 337 * Callback function for initializing the blobstore. 338 */ 339 static void 340 bs_init_complete(void *cb_arg, struct spdk_blob_store *bs, 341 int bserrno) 342 { 343 struct hello_context_t *hello_context = cb_arg; 344 345 SPDK_NOTICELOG("entry\n"); 346 if (bserrno) { 347 unload_bs(hello_context, "Error initing the blobstore", 348 bserrno); 349 return; 350 } 351 352 hello_context->bs = bs; 353 SPDK_NOTICELOG("blobstore: %p\n", hello_context->bs); 354 /* 355 * We will use the io_unit size in allocating buffers, etc., later 356 * so we'll just save it in out context buffer here. 357 */ 358 hello_context->io_unit_size = spdk_bs_get_io_unit_size(hello_context->bs); 359 360 /* 361 * The blobstore has been initialized, let's create a blob. 362 * Note that we could pass a message back to ourselves using 363 * spdk_thread_send_msg() if we wanted to keep our processing 364 * time limited. 365 */ 366 create_blob(hello_context); 367 } 368 369 static void 370 base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 371 void *event_ctx) 372 { 373 SPDK_WARNLOG("Unsupported bdev event: type %d\n", type); 374 } 375 376 /* 377 * Our initial event that kicks off everything from main(). 378 */ 379 static void 380 hello_start(void *arg1) 381 { 382 struct hello_context_t *hello_context = arg1; 383 struct spdk_bs_dev *bs_dev = NULL; 384 int rc; 385 386 SPDK_NOTICELOG("entry\n"); 387 388 /* 389 * In this example, use our malloc (RAM) disk configured via 390 * hello_blob.json that was passed in when we started the 391 * SPDK app framework. 392 * 393 * spdk_bs_init() requires us to fill out the structure 394 * spdk_bs_dev with a set of callbacks. These callbacks 395 * implement read, write, and other operations on the 396 * underlying disks. As a convenience, a utility function 397 * is provided that creates an spdk_bs_dev that implements 398 * all of the callbacks by forwarding the I/O to the 399 * SPDK bdev layer. Other helper functions are also 400 * available in the blob lib in blob_bdev.c that simply 401 * make it easier to layer blobstore on top of a bdev. 402 * However blobstore can be more tightly integrated into 403 * any lower layer, such as NVMe for example. 404 */ 405 rc = spdk_bdev_create_bs_dev_ext("Malloc0", base_bdev_event_cb, NULL, &bs_dev); 406 if (rc != 0) { 407 SPDK_ERRLOG("Could not create blob bdev, %s!!\n", 408 spdk_strerror(-rc)); 409 spdk_app_stop(-1); 410 return; 411 } 412 413 spdk_bs_init(bs_dev, NULL, bs_init_complete, hello_context); 414 } 415 416 int 417 main(int argc, char **argv) 418 { 419 struct spdk_app_opts opts = {}; 420 int rc = 0; 421 struct hello_context_t *hello_context = NULL; 422 423 SPDK_NOTICELOG("entry\n"); 424 425 /* Set default values in opts structure. */ 426 spdk_app_opts_init(&opts, sizeof(opts)); 427 428 /* 429 * Setup a few specifics before we init, for most SPDK cmd line 430 * apps, the config file will be passed in as an arg but to make 431 * this example super simple we just hardcode it. We also need to 432 * specify a name for the app. 433 */ 434 opts.name = "hello_blob"; 435 opts.json_config_file = argv[1]; 436 437 438 /* 439 * Now we'll allocate and initialize the blobstore itself. We 440 * can pass in an spdk_bs_opts if we want something other than 441 * the defaults (cluster size, etc), but here we'll just take the 442 * defaults. We'll also pass in a struct that we'll use for 443 * callbacks so we've got efficient bookkeeping of what we're 444 * creating. This is an async operation and bs_init_complete() 445 * will be called when it is complete. 446 */ 447 hello_context = calloc(1, sizeof(struct hello_context_t)); 448 if (hello_context != NULL) { 449 /* 450 * spdk_app_start() will block running hello_start() until 451 * spdk_app_stop() is called by someone (not simply when 452 * hello_start() returns), or if an error occurs during 453 * spdk_app_start() before hello_start() runs. 454 */ 455 rc = spdk_app_start(&opts, hello_start, hello_context); 456 if (rc) { 457 SPDK_NOTICELOG("ERROR!\n"); 458 } else { 459 SPDK_NOTICELOG("SUCCESS!\n"); 460 } 461 /* Free up memory that we allocated */ 462 hello_cleanup(hello_context); 463 } else { 464 SPDK_ERRLOG("Could not alloc hello_context struct!!\n"); 465 rc = -ENOMEM; 466 } 467 468 /* Gracefully close out all of the SPDK subsystems. */ 469 spdk_app_fini(); 470 return rc; 471 } 472