xref: /spdk/examples/blob/hello_world/hello_blob.c (revision f86f10757912918b8ba7b4b3bfdab1cd4c2d180c)
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 page_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_dma_free(hello_context->read_buff);
65 	spdk_dma_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->page_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_dma_malloc(hello_context->page_size,
183 				   0x1000, NULL);
184 	if (hello_context->read_buff == NULL) {
185 		unload_bs(hello_context, "Error in memory allocation",
186 			  -ENOMEM);
187 		return;
188 	}
189 
190 	/* Issue the read and compare the results in the callback. */
191 	spdk_bs_io_read_blob(hello_context->blob, hello_context->channel,
192 			     hello_context->read_buff, 0, 1, read_complete,
193 			     hello_context);
194 }
195 
196 /*
197  * Callback function for writing a blob.
198  */
199 static void
200 write_complete(void *arg1, int bserrno)
201 {
202 	struct hello_context_t *hello_context = arg1;
203 
204 	SPDK_NOTICELOG("entry\n");
205 	if (bserrno) {
206 		unload_bs(hello_context, "Error in write completion",
207 			  bserrno);
208 		return;
209 	}
210 
211 	/* Now let's read back what we wrote and make sure it matches. */
212 	read_blob(hello_context);
213 }
214 
215 /*
216  * Function for writing to a blob.
217  */
218 static void
219 blob_write(struct hello_context_t *hello_context)
220 {
221 	SPDK_NOTICELOG("entry\n");
222 
223 	/*
224 	 * Buffers for data transfer need to be allocated via SPDK. We will
225 	 * tranfer 1 page of 4K aligned data at offset 0 in the blob.
226 	 */
227 	hello_context->write_buff = spdk_dma_malloc(hello_context->page_size,
228 				    0x1000, NULL);
229 	if (hello_context->write_buff == NULL) {
230 		unload_bs(hello_context, "Error in allocating memory",
231 			  -ENOMEM);
232 		return;
233 	}
234 	memset(hello_context->write_buff, 0x5a, hello_context->page_size);
235 
236 	/* Now we have to allocate a channel. */
237 	hello_context->channel = spdk_bs_alloc_io_channel(hello_context->bs);
238 	if (hello_context->channel == NULL) {
239 		unload_bs(hello_context, "Error in allocating channel",
240 			  -ENOMEM);
241 		return;
242 	}
243 
244 	/* Let's perform the write, 1 page at offset 0. */
245 	spdk_bs_io_write_blob(hello_context->blob, hello_context->channel,
246 			      hello_context->write_buff,
247 			      0, 1, write_complete, hello_context);
248 }
249 
250 /*
251  * Callback function for sync'ing metadata.
252  */
253 static void
254 sync_complete(void *arg1, int bserrno)
255 {
256 	struct hello_context_t *hello_context = arg1;
257 
258 	SPDK_NOTICELOG("entry\n");
259 	if (bserrno) {
260 		unload_bs(hello_context, "Error in sync callback",
261 			  bserrno);
262 		return;
263 	}
264 
265 	/* Blob has been created & sized & MD sync'd, let's write to it. */
266 	blob_write(hello_context);
267 }
268 
269 /*
270  * Callback function for opening a blob.
271  */
272 static void
273 open_complete(void *cb_arg, struct spdk_blob *blob, int bserrno)
274 {
275 	struct hello_context_t *hello_context = cb_arg;
276 	uint64_t free = 0;
277 	uint64_t total = 0;
278 	int rc = 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 	rc = spdk_blob_resize(hello_context->blob, free);
301 	if (rc) {
302 		unload_bs(hello_context, "Error in blob resize",
303 			  bserrno);
304 		return;
305 	}
306 
307 	total = spdk_blob_get_num_clusters(hello_context->blob);
308 	SPDK_NOTICELOG("resized blob now has USED clusters of %" PRIu64 "\n",
309 		       total);
310 
311 	/*
312 	 * Metadata is stored in volatile memory for performance
313 	 * reasons and therefore needs to be synchronized with
314 	 * non-volatile storage to make it persistent. This can be
315 	 * done manually, as shown here, or if not it will be done
316 	 * automatically when the blob is closed. It is always a
317 	 * good idea to sync after making metadata changes unless
318 	 * it has an unacceptable impact on application performance.
319 	 */
320 	spdk_blob_sync_md(hello_context->blob, sync_complete, hello_context);
321 }
322 
323 /*
324  * Callback function for creating a blob.
325  */
326 static void
327 blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno)
328 {
329 	struct hello_context_t *hello_context = arg1;
330 
331 	SPDK_NOTICELOG("entry\n");
332 	if (bserrno) {
333 		unload_bs(hello_context, "Error in blob create callback",
334 			  bserrno);
335 		return;
336 	}
337 
338 	hello_context->blobid = blobid;
339 	SPDK_NOTICELOG("new blob id %" PRIu64 "\n", hello_context->blobid);
340 
341 	/* We have to open the blob before we can do things like resize. */
342 	spdk_bs_open_blob(hello_context->bs, hello_context->blobid,
343 			  open_complete, hello_context);
344 }
345 
346 /*
347  * Function for creating a blob.
348  */
349 static void
350 create_blob(struct hello_context_t *hello_context)
351 {
352 	SPDK_NOTICELOG("entry\n");
353 	spdk_bs_create_blob(hello_context->bs, blob_create_complete, hello_context);
354 }
355 
356 /*
357  * Callback function for initializing the blobstore.
358  */
359 static void
360 bs_init_complete(void *cb_arg, struct spdk_blob_store *bs,
361 		 int bserrno)
362 {
363 	struct hello_context_t *hello_context = cb_arg;
364 
365 	SPDK_NOTICELOG("entry\n");
366 	if (bserrno) {
367 		unload_bs(hello_context, "Error init'ing the blobstore",
368 			  bserrno);
369 		return;
370 	}
371 
372 	hello_context->bs = bs;
373 	SPDK_NOTICELOG("blobstore: %p\n", hello_context->bs);
374 	/*
375 	 * We will use the page size in allocating buffers, etc., later
376 	 * so we'll just save it in out context buffer here.
377 	 */
378 	hello_context->page_size = spdk_bs_get_page_size(hello_context->bs);
379 
380 	/*
381 	 * The blostore has been initialized, let's create a blob.
382 	 * Note that we could allcoate an SPDK event and use
383 	 * spdk_event_call() to schedule it if we wanted to keep
384 	 * our events as limited as possible wrt the amount of
385 	 * work that they do.
386 	 */
387 	create_blob(hello_context);
388 }
389 
390 /*
391  * Our initial event that kicks off everything from main().
392  */
393 static void
394 hello_start(void *arg1, void *arg2)
395 {
396 	struct hello_context_t *hello_context = arg1;
397 	struct spdk_bdev *bdev = NULL;
398 	struct spdk_bs_dev *bs_dev = NULL;
399 
400 	SPDK_NOTICELOG("entry\n");
401 	/*
402 	 * Get the bdev. For this example it is our malloc (RAM)
403 	 * disk configured via hello_blob.conf that was passed
404 	 * in when we started the SPDK app framework so we can
405 	 * get it via its name.
406 	 */
407 	bdev = spdk_bdev_get_by_name("Malloc0");
408 	if (bdev == NULL) {
409 		SPDK_ERRLOG("Could not find a bdev\n");
410 		spdk_app_stop(-1);
411 		return;
412 	}
413 
414 	/*
415 	 * spdk_bs_init() requires us to fill out the structure
416 	 * spdk_bs_dev with a set of callbacks. These callbacks
417 	 * implement read, write, and other operations on the
418 	 * underlying disks. As a convenience, a utility function
419 	 * is provided that creates an spdk_bs_dev that implements
420 	 * all of the callbacks by forwarding the I/O to the
421 	 * SPDK bdev layer. Other helper functions are also
422 	 * available in the blob lib in blob_bdev.c that simply
423 	 * make it easier to layer blobstore on top of a bdev.
424 	 * However blobstore can be more tightly integrated into
425 	 * any lower layer, such as NVMe for example.
426 	 */
427 	bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
428 	if (bs_dev == NULL) {
429 		SPDK_ERRLOG("Could not create blob bdev!!\n");
430 		spdk_app_stop(-1);
431 		return;
432 	}
433 
434 	spdk_bs_init(bs_dev, NULL, bs_init_complete, hello_context);
435 }
436 
437 int
438 main(int argc, char **argv)
439 {
440 	struct spdk_app_opts opts = {};
441 	int rc = 0;
442 	struct hello_context_t *hello_context = NULL;
443 
444 	SPDK_NOTICELOG("entry\n");
445 
446 	/* Set default values in opts structure. */
447 	spdk_app_opts_init(&opts);
448 
449 	/*
450 	 * Setup a few specifics before we init, for most SPDK cmd line
451 	 * apps, the config file will be passed in as an arg but to make
452 	 * this example super simple we just hardcode it. We also need to
453 	 * specify a name for the app.
454 	 */
455 	opts.name = "hello_blob";
456 	opts.config_file = "hello_blob.conf";
457 
458 
459 	/*
460 	 * Now we'll allocate and intialize the blobstore itself. We
461 	 * can pass in an spdk_bs_opts if we want something other than
462 	 * the defaults (cluster size, etc), but here we'll just take the
463 	 * defaults.  We'll also pass in a struct that we'll use for
464 	 * callbacks so we've got efficient bookeeping of what we're
465 	 * creating. This is an async operation and bs_init_complete()
466 	 * will be called when it is complete.
467 	 */
468 	hello_context = calloc(1, sizeof(struct hello_context_t));
469 	if (hello_context != NULL) {
470 		/*
471 		 * spdk_app_start() will block running hello_start() until
472 		 * spdk_app_stop() is called by someone (not simply when
473 		 * hello_start() returns)
474 		 */
475 		rc = spdk_app_start(&opts, hello_start, hello_context, NULL);
476 		if (rc) {
477 			SPDK_NOTICELOG("ERROR!\n");
478 		} else {
479 			SPDK_NOTICELOG("SUCCCESS!\n");
480 		}
481 		/* Free up memory that we allocated */
482 		hello_cleanup(hello_context);
483 	} else {
484 		SPDK_ERRLOG("Could not alloc hello_context struct!!\n");
485 		rc = -ENOMEM;
486 	}
487 
488 	/* Gracefully close out all of the SPDK subsystems. */
489 	spdk_app_fini();
490 	return rc;
491 }
492