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
hello_cleanup(struct hello_context_t * hello_context)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
unload_complete(void * cb_arg,int bserrno)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
unload_bs(struct hello_context_t * hello_context,char * msg,int bserrno)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
delete_complete(void * arg1,int bserrno)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
delete_blob(void * arg1,int bserrno)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
read_complete(void * arg1,int bserrno)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
read_blob(struct hello_context_t * hello_context)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
write_complete(void * arg1,int bserrno)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
blob_write(struct hello_context_t * hello_context)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
sync_complete(void * arg1,int bserrno)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
resize_complete(void * cb_arg,int bserrno)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
open_complete(void * cb_arg,struct spdk_blob * blob,int bserrno)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
blob_create_complete(void * arg1,spdk_blob_id blobid,int bserrno)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
create_blob(struct hello_context_t * hello_context)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
bs_init_complete(void * cb_arg,struct spdk_blob_store * bs,int bserrno)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
base_bdev_event_cb(enum spdk_bdev_event_type type,struct spdk_bdev * bdev,void * event_ctx)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
hello_start(void * arg1)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
main(int argc,char ** argv)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 opts.rpc_addr = NULL;
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