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