xref: /spdk/examples/blob/hello_world/hello_blob.c (revision fecffda6ecf8853b82edccde429b68252f0a62c5)
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