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