Branch data Line data Source code
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 : 0 : hello_cleanup(struct hello_context_t *hello_context)
36 : : {
37 : 0 : spdk_free(hello_context->read_buff);
38 : 0 : spdk_free(hello_context->write_buff);
39 : 0 : free(hello_context);
40 : 0 : }
41 : :
42 : : /*
43 : : * Callback routine for the blobstore unload.
44 : : */
45 : : static void
46 : 0 : unload_complete(void *cb_arg, int bserrno)
47 : : {
48 : 0 : struct hello_context_t *hello_context = cb_arg;
49 : :
50 : 0 : SPDK_NOTICELOG("entry\n");
51 [ # # ]: 0 : if (bserrno) {
52 : 0 : SPDK_ERRLOG("Error %d unloading the bobstore\n", bserrno);
53 : 0 : hello_context->rc = bserrno;
54 : : }
55 : :
56 : 0 : spdk_app_stop(hello_context->rc);
57 : 0 : }
58 : :
59 : : /*
60 : : * Unload the blobstore, cleaning up as needed.
61 : : */
62 : : static void
63 : 0 : unload_bs(struct hello_context_t *hello_context, char *msg, int bserrno)
64 : : {
65 [ # # ]: 0 : if (bserrno) {
66 : 0 : SPDK_ERRLOG("%s (err %d)\n", msg, bserrno);
67 : 0 : hello_context->rc = bserrno;
68 : : }
69 [ # # ]: 0 : if (hello_context->bs) {
70 [ # # ]: 0 : if (hello_context->channel) {
71 : 0 : spdk_bs_free_io_channel(hello_context->channel);
72 : : }
73 : 0 : spdk_bs_unload(hello_context->bs, unload_complete, hello_context);
74 : : } else {
75 : 0 : spdk_app_stop(bserrno);
76 : : }
77 : 0 : }
78 : :
79 : : /*
80 : : * Callback routine for the deletion of a blob.
81 : : */
82 : : static void
83 : 0 : delete_complete(void *arg1, int bserrno)
84 : : {
85 : 0 : struct hello_context_t *hello_context = arg1;
86 : :
87 : 0 : SPDK_NOTICELOG("entry\n");
88 [ # # ]: 0 : if (bserrno) {
89 : 0 : unload_bs(hello_context, "Error in delete completion",
90 : : bserrno);
91 : 0 : return;
92 : : }
93 : :
94 : : /* We're all done, we can unload the blobstore. */
95 : 0 : unload_bs(hello_context, "", 0);
96 : : }
97 : :
98 : : /*
99 : : * Function for deleting a blob.
100 : : */
101 : : static void
102 : 0 : delete_blob(void *arg1, int bserrno)
103 : : {
104 : 0 : struct hello_context_t *hello_context = arg1;
105 : :
106 : 0 : SPDK_NOTICELOG("entry\n");
107 [ # # ]: 0 : if (bserrno) {
108 : 0 : unload_bs(hello_context, "Error in close completion",
109 : : bserrno);
110 : 0 : return;
111 : : }
112 : :
113 : 0 : 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 : 0 : read_complete(void *arg1, int bserrno)
122 : : {
123 : 0 : struct hello_context_t *hello_context = arg1;
124 : 0 : int match_res = -1;
125 : :
126 : 0 : SPDK_NOTICELOG("entry\n");
127 [ # # ]: 0 : if (bserrno) {
128 : 0 : unload_bs(hello_context, "Error in read completion",
129 : : bserrno);
130 : 0 : return;
131 : : }
132 : :
133 : : /* Now let's make sure things match. */
134 [ # # # # ]: 0 : match_res = memcmp(hello_context->write_buff, hello_context->read_buff,
135 : : hello_context->io_unit_size);
136 [ # # ]: 0 : if (match_res) {
137 : 0 : unload_bs(hello_context, "Error in data compare", -1);
138 : 0 : return;
139 : : } else {
140 : 0 : SPDK_NOTICELOG("read SUCCESS and data matches!\n");
141 : : }
142 : :
143 : : /* Now let's close it and delete the blob in the callback. */
144 : 0 : spdk_blob_close(hello_context->blob, delete_blob, hello_context);
145 : : }
146 : :
147 : : /*
148 : : * Function for reading a blob.
149 : : */
150 : : static void
151 : 0 : read_blob(struct hello_context_t *hello_context)
152 : : {
153 : 0 : SPDK_NOTICELOG("entry\n");
154 : :
155 : 0 : hello_context->read_buff = spdk_malloc(hello_context->io_unit_size,
156 : : 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY,
157 : : SPDK_MALLOC_DMA);
158 [ # # ]: 0 : if (hello_context->read_buff == NULL) {
159 : 0 : unload_bs(hello_context, "Error in memory allocation",
160 : : -ENOMEM);
161 : 0 : return;
162 : : }
163 : :
164 : : /* Issue the read and compare the results in the callback. */
165 : 0 : spdk_blob_io_read(hello_context->blob, hello_context->channel,
166 : 0 : 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 : 0 : write_complete(void *arg1, int bserrno)
175 : : {
176 : 0 : struct hello_context_t *hello_context = arg1;
177 : :
178 : 0 : SPDK_NOTICELOG("entry\n");
179 [ # # ]: 0 : if (bserrno) {
180 : 0 : unload_bs(hello_context, "Error in write completion",
181 : : bserrno);
182 : 0 : return;
183 : : }
184 : :
185 : : /* Now let's read back what we wrote and make sure it matches. */
186 : 0 : read_blob(hello_context);
187 : : }
188 : :
189 : : /*
190 : : * Function for writing to a blob.
191 : : */
192 : : static void
193 : 0 : blob_write(struct hello_context_t *hello_context)
194 : : {
195 : 0 : 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 : 0 : hello_context->write_buff = spdk_malloc(hello_context->io_unit_size,
202 : : 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY,
203 : : SPDK_MALLOC_DMA);
204 [ # # ]: 0 : if (hello_context->write_buff == NULL) {
205 : 0 : unload_bs(hello_context, "Error in allocating memory",
206 : : -ENOMEM);
207 : 0 : return;
208 : : }
209 [ # # ]: 0 : memset(hello_context->write_buff, 0x5a, hello_context->io_unit_size);
210 : :
211 : : /* Now we have to allocate a channel. */
212 : 0 : hello_context->channel = spdk_bs_alloc_io_channel(hello_context->bs);
213 [ # # ]: 0 : if (hello_context->channel == NULL) {
214 : 0 : unload_bs(hello_context, "Error in allocating channel",
215 : : -ENOMEM);
216 : 0 : return;
217 : : }
218 : :
219 : : /* Let's perform the write, 1 io_unit at offset 0. */
220 : 0 : spdk_blob_io_write(hello_context->blob, hello_context->channel,
221 : 0 : 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 : 0 : sync_complete(void *arg1, int bserrno)
230 : : {
231 : 0 : struct hello_context_t *hello_context = arg1;
232 : :
233 : 0 : SPDK_NOTICELOG("entry\n");
234 [ # # ]: 0 : if (bserrno) {
235 : 0 : unload_bs(hello_context, "Error in sync callback",
236 : : bserrno);
237 : 0 : return;
238 : : }
239 : :
240 : : /* Blob has been created & sized & MD sync'd, let's write to it. */
241 : 0 : blob_write(hello_context);
242 : : }
243 : :
244 : : static void
245 : 0 : resize_complete(void *cb_arg, int bserrno)
246 : : {
247 : 0 : struct hello_context_t *hello_context = cb_arg;
248 : 0 : uint64_t total = 0;
249 : :
250 [ # # ]: 0 : if (bserrno) {
251 : 0 : unload_bs(hello_context, "Error in blob resize", bserrno);
252 : 0 : return;
253 : : }
254 : :
255 : 0 : total = spdk_blob_get_num_clusters(hello_context->blob);
256 : 0 : 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 : 0 : 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 : 0 : open_complete(void *cb_arg, struct spdk_blob *blob, int bserrno)
276 : : {
277 : 0 : struct hello_context_t *hello_context = cb_arg;
278 : 0 : uint64_t free = 0;
279 : :
280 : 0 : SPDK_NOTICELOG("entry\n");
281 [ # # ]: 0 : if (bserrno) {
282 : 0 : unload_bs(hello_context, "Error in open completion",
283 : : bserrno);
284 : 0 : return;
285 : : }
286 : :
287 : :
288 : 0 : hello_context->blob = blob;
289 : 0 : free = spdk_bs_free_cluster_count(hello_context->bs);
290 : 0 : 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 : 0 : 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 : 0 : blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno)
308 : : {
309 : 0 : struct hello_context_t *hello_context = arg1;
310 : :
311 : 0 : SPDK_NOTICELOG("entry\n");
312 [ # # ]: 0 : if (bserrno) {
313 : 0 : unload_bs(hello_context, "Error in blob create callback",
314 : : bserrno);
315 : 0 : return;
316 : : }
317 : :
318 : 0 : hello_context->blobid = blobid;
319 : 0 : 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 : 0 : 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 : 0 : create_blob(struct hello_context_t *hello_context)
331 : : {
332 : 0 : SPDK_NOTICELOG("entry\n");
333 : 0 : spdk_bs_create_blob(hello_context->bs, blob_create_complete, hello_context);
334 : 0 : }
335 : :
336 : : /*
337 : : * Callback function for initializing the blobstore.
338 : : */
339 : : static void
340 : 0 : bs_init_complete(void *cb_arg, struct spdk_blob_store *bs,
341 : : int bserrno)
342 : : {
343 : 0 : struct hello_context_t *hello_context = cb_arg;
344 : :
345 : 0 : SPDK_NOTICELOG("entry\n");
346 [ # # ]: 0 : if (bserrno) {
347 : 0 : unload_bs(hello_context, "Error initing the blobstore",
348 : : bserrno);
349 : 0 : return;
350 : : }
351 : :
352 : 0 : hello_context->bs = bs;
353 : 0 : 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 : 0 : 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 : 0 : create_blob(hello_context);
367 : : }
368 : :
369 : : static void
370 : 0 : base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
371 : : void *event_ctx)
372 : : {
373 : 0 : SPDK_WARNLOG("Unsupported bdev event: type %d\n", type);
374 : 0 : }
375 : :
376 : : /*
377 : : * Our initial event that kicks off everything from main().
378 : : */
379 : : static void
380 : 0 : hello_start(void *arg1)
381 : : {
382 : 0 : struct hello_context_t *hello_context = arg1;
383 : 0 : struct spdk_bs_dev *bs_dev = NULL;
384 : : int rc;
385 : :
386 : 0 : 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 : 0 : rc = spdk_bdev_create_bs_dev_ext("Malloc0", base_bdev_event_cb, NULL, &bs_dev);
406 [ # # ]: 0 : if (rc != 0) {
407 : 0 : SPDK_ERRLOG("Could not create blob bdev, %s!!\n",
408 : : spdk_strerror(-rc));
409 : 0 : spdk_app_stop(-1);
410 : 0 : return;
411 : : }
412 : :
413 : 0 : spdk_bs_init(bs_dev, NULL, bs_init_complete, hello_context);
414 : : }
415 : :
416 : : int
417 : 0 : main(int argc, char **argv)
418 : : {
419 : 0 : struct spdk_app_opts opts = {};
420 : 0 : int rc = 0;
421 : 0 : struct hello_context_t *hello_context = NULL;
422 : :
423 : 0 : SPDK_NOTICELOG("entry\n");
424 : :
425 : : /* Set default values in opts structure. */
426 : 0 : 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 : 0 : opts.name = "hello_blob";
435 : 0 : 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 : 0 : hello_context = calloc(1, sizeof(struct hello_context_t));
448 [ # # ]: 0 : 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 : 0 : rc = spdk_app_start(&opts, hello_start, hello_context);
456 [ # # ]: 0 : if (rc) {
457 : 0 : SPDK_NOTICELOG("ERROR!\n");
458 : : } else {
459 : 0 : SPDK_NOTICELOG("SUCCESS!\n");
460 : : }
461 : : /* Free up memory that we allocated */
462 : 0 : hello_cleanup(hello_context);
463 : : } else {
464 : 0 : SPDK_ERRLOG("Could not alloc hello_context struct!!\n");
465 : 0 : rc = -ENOMEM;
466 : : }
467 : :
468 : : /* Gracefully close out all of the SPDK subsystems. */
469 : 0 : spdk_app_fini();
470 : 0 : return rc;
471 : : }
|