diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/spdk/examples/blob/hello_world | |
parent | Initial commit. (diff) | |
download | ceph-upstream.tar.xz ceph-upstream.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/examples/blob/hello_world')
-rw-r--r-- | src/spdk/examples/blob/hello_world/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/examples/blob/hello_world/Makefile | 57 | ||||
-rw-r--r-- | src/spdk/examples/blob/hello_world/hello_blob.c | 496 | ||||
-rw-r--r-- | src/spdk/examples/blob/hello_world/hello_blob.conf | 3 |
4 files changed, 557 insertions, 0 deletions
diff --git a/src/spdk/examples/blob/hello_world/.gitignore b/src/spdk/examples/blob/hello_world/.gitignore new file mode 100644 index 00000000..683a2255 --- /dev/null +++ b/src/spdk/examples/blob/hello_world/.gitignore @@ -0,0 +1 @@ +hello_blob diff --git a/src/spdk/examples/blob/hello_world/Makefile b/src/spdk/examples/blob/hello_world/Makefile new file mode 100644 index 00000000..7c567fcb --- /dev/null +++ b/src/spdk/examples/blob/hello_world/Makefile @@ -0,0 +1,57 @@ +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk +include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk + +APP = hello_blob + +C_SRCS := hello_blob.c + +SPDK_LIB_LIST = event_bdev event_copy +SPDK_LIB_LIST += blobfs blob bdev blob_bdev copy event thread util conf trace \ + log jsonrpc json rpc + +LIBS += $(COPY_MODULES_LINKER_ARGS) $(BLOCKDEV_MODULES_LINKER_ARGS) $(SOCK_MODULES_LINKER_ARGS) +LIBS += $(SPDK_LIB_LINKER_ARGS) $(ENV_LINKER_ARGS) + +all : $(APP) + @: + +$(APP) : $(OBJS) $(SPDK_LIB_FILES) $(COPY_MODULES_FILES) $(BLOCKDEV_MODULES_FILES) $(SOCK_MODULES_FILES) $(LINKER_MODULES) $(ENV_LIBS) + $(LINK_C) + +clean : + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/src/spdk/examples/blob/hello_world/hello_blob.c b/src/spdk/examples/blob/hello_world/hello_blob.c new file mode 100644 index 00000000..ffcc2976 --- /dev/null +++ b/src/spdk/examples/blob/hello_world/hello_blob.c @@ -0,0 +1,496 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk/bdev.h" +#include "spdk/env.h" +#include "spdk/event.h" +#include "spdk/blob_bdev.h" +#include "spdk/blob.h" +#include "spdk/log.h" + +/* + * We'll use this struct to gather housekeeping hello_context to pass between + * our events and callbacks. + */ +struct hello_context_t { + struct spdk_blob_store *bs; + struct spdk_blob *blob; + spdk_blob_id blobid; + struct spdk_io_channel *channel; + uint8_t *read_buff; + uint8_t *write_buff; + uint64_t page_size; + int rc; +}; + +/* + * Free up memory that we allocated. + */ +static void +hello_cleanup(struct hello_context_t *hello_context) +{ + spdk_dma_free(hello_context->read_buff); + spdk_dma_free(hello_context->write_buff); + free(hello_context); +} + +/* + * Callback routine for the blobstore unload. + */ +static void +unload_complete(void *cb_arg, int bserrno) +{ + struct hello_context_t *hello_context = cb_arg; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + SPDK_ERRLOG("Error %d unloading the bobstore\n", bserrno); + hello_context->rc = bserrno; + } + + spdk_app_stop(hello_context->rc); +} + +/* + * Unload the blobstore, cleaning up as needed. + */ +static void +unload_bs(struct hello_context_t *hello_context, char *msg, int bserrno) +{ + if (bserrno) { + SPDK_ERRLOG("%s (err %d)\n", msg, bserrno); + hello_context->rc = bserrno; + } + if (hello_context->bs) { + if (hello_context->channel) { + spdk_bs_free_io_channel(hello_context->channel); + } + spdk_bs_unload(hello_context->bs, unload_complete, hello_context); + } else { + spdk_app_stop(bserrno); + } +} + +/* + * Callback routine for the deletion of a blob. + */ +static void +delete_complete(void *arg1, int bserrno) +{ + struct hello_context_t *hello_context = arg1; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in delete completion", + bserrno); + return; + } + + /* We're all done, we can unload the blobstore. */ + unload_bs(hello_context, "", 0); +} + +/* + * Function for deleting a blob. + */ +static void +delete_blob(void *arg1, int bserrno) +{ + struct hello_context_t *hello_context = arg1; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in close completion", + bserrno); + return; + } + + spdk_bs_delete_blob(hello_context->bs, hello_context->blobid, + delete_complete, hello_context); +} + +/* + * Callback function for reading a blob. + */ +static void +read_complete(void *arg1, int bserrno) +{ + struct hello_context_t *hello_context = arg1; + int match_res = -1; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in read completion", + bserrno); + return; + } + + /* Now let's make sure things match. */ + match_res = memcmp(hello_context->write_buff, hello_context->read_buff, + hello_context->page_size); + if (match_res) { + unload_bs(hello_context, "Error in data compare", -1); + return; + } else { + SPDK_NOTICELOG("read SUCCESS and data matches!\n"); + } + + /* Now let's close it and delete the blob in the callback. */ + spdk_blob_close(hello_context->blob, delete_blob, hello_context); +} + +/* + * Function for reading a blob. + */ +static void +read_blob(struct hello_context_t *hello_context) +{ + SPDK_NOTICELOG("entry\n"); + + hello_context->read_buff = spdk_dma_malloc(hello_context->page_size, + 0x1000, NULL); + if (hello_context->read_buff == NULL) { + unload_bs(hello_context, "Error in memory allocation", + -ENOMEM); + return; + } + + /* Issue the read and compare the results in the callback. */ + spdk_blob_io_read(hello_context->blob, hello_context->channel, + hello_context->read_buff, 0, 1, read_complete, + hello_context); +} + +/* + * Callback function for writing a blob. + */ +static void +write_complete(void *arg1, int bserrno) +{ + struct hello_context_t *hello_context = arg1; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in write completion", + bserrno); + return; + } + + /* Now let's read back what we wrote and make sure it matches. */ + read_blob(hello_context); +} + +/* + * Function for writing to a blob. + */ +static void +blob_write(struct hello_context_t *hello_context) +{ + SPDK_NOTICELOG("entry\n"); + + /* + * Buffers for data transfer need to be allocated via SPDK. We will + * tranfer 1 page of 4K aligned data at offset 0 in the blob. + */ + hello_context->write_buff = spdk_dma_malloc(hello_context->page_size, + 0x1000, NULL); + if (hello_context->write_buff == NULL) { + unload_bs(hello_context, "Error in allocating memory", + -ENOMEM); + return; + } + memset(hello_context->write_buff, 0x5a, hello_context->page_size); + + /* Now we have to allocate a channel. */ + hello_context->channel = spdk_bs_alloc_io_channel(hello_context->bs); + if (hello_context->channel == NULL) { + unload_bs(hello_context, "Error in allocating channel", + -ENOMEM); + return; + } + + /* Let's perform the write, 1 page at offset 0. */ + spdk_blob_io_write(hello_context->blob, hello_context->channel, + hello_context->write_buff, + 0, 1, write_complete, hello_context); +} + +/* + * Callback function for sync'ing metadata. + */ +static void +sync_complete(void *arg1, int bserrno) +{ + struct hello_context_t *hello_context = arg1; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in sync callback", + bserrno); + return; + } + + /* Blob has been created & sized & MD sync'd, let's write to it. */ + blob_write(hello_context); +} + +static void +resize_complete(void *cb_arg, int bserrno) +{ + struct hello_context_t *hello_context = cb_arg; + uint64_t total = 0; + + if (bserrno) { + unload_bs(hello_context, "Error in blob resize", bserrno); + return; + } + + total = spdk_blob_get_num_clusters(hello_context->blob); + SPDK_NOTICELOG("resized blob now has USED clusters of %" PRIu64 "\n", + total); + + /* + * Metadata is stored in volatile memory for performance + * reasons and therefore needs to be synchronized with + * non-volatile storage to make it persistent. This can be + * done manually, as shown here, or if not it will be done + * automatically when the blob is closed. It is always a + * good idea to sync after making metadata changes unless + * it has an unacceptable impact on application performance. + */ + spdk_blob_sync_md(hello_context->blob, sync_complete, hello_context); +} + +/* + * Callback function for opening a blob. + */ +static void +open_complete(void *cb_arg, struct spdk_blob *blob, int bserrno) +{ + struct hello_context_t *hello_context = cb_arg; + uint64_t free = 0; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in open completion", + bserrno); + return; + } + + + hello_context->blob = blob; + free = spdk_bs_free_cluster_count(hello_context->bs); + SPDK_NOTICELOG("blobstore has FREE clusters of %" PRIu64 "\n", + free); + + /* + * Before we can use our new blob, we have to resize it + * as the initial size is 0. For this example we'll use the + * full size of the blobstore but it would be expected that + * there'd usually be many blobs of various sizes. The resize + * unit is a cluster. + */ + spdk_blob_resize(hello_context->blob, free, resize_complete, hello_context); +} + +/* + * Callback function for creating a blob. + */ +static void +blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno) +{ + struct hello_context_t *hello_context = arg1; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error in blob create callback", + bserrno); + return; + } + + hello_context->blobid = blobid; + SPDK_NOTICELOG("new blob id %" PRIu64 "\n", hello_context->blobid); + + /* We have to open the blob before we can do things like resize. */ + spdk_bs_open_blob(hello_context->bs, hello_context->blobid, + open_complete, hello_context); +} + +/* + * Function for creating a blob. + */ +static void +create_blob(struct hello_context_t *hello_context) +{ + SPDK_NOTICELOG("entry\n"); + spdk_bs_create_blob(hello_context->bs, blob_create_complete, hello_context); +} + +/* + * Callback function for initializing the blobstore. + */ +static void +bs_init_complete(void *cb_arg, struct spdk_blob_store *bs, + int bserrno) +{ + struct hello_context_t *hello_context = cb_arg; + + SPDK_NOTICELOG("entry\n"); + if (bserrno) { + unload_bs(hello_context, "Error init'ing the blobstore", + bserrno); + return; + } + + hello_context->bs = bs; + SPDK_NOTICELOG("blobstore: %p\n", hello_context->bs); + /* + * We will use the page size in allocating buffers, etc., later + * so we'll just save it in out context buffer here. + */ + hello_context->page_size = spdk_bs_get_page_size(hello_context->bs); + + /* + * The blostore has been initialized, let's create a blob. + * Note that we could pass a message back to ourselves using + * spdk_thread_send_msg() if we wanted to keep our processing + * time limited. + */ + create_blob(hello_context); +} + +/* + * Our initial event that kicks off everything from main(). + */ +static void +hello_start(void *arg1, void *arg2) +{ + struct hello_context_t *hello_context = arg1; + struct spdk_bdev *bdev = NULL; + struct spdk_bs_dev *bs_dev = NULL; + + SPDK_NOTICELOG("entry\n"); + /* + * Get the bdev. For this example it is our malloc (RAM) + * disk configured via hello_blob.conf that was passed + * in when we started the SPDK app framework so we can + * get it via its name. + */ + bdev = spdk_bdev_get_by_name("Malloc0"); + if (bdev == NULL) { + SPDK_ERRLOG("Could not find a bdev\n"); + spdk_app_stop(-1); + return; + } + + /* + * spdk_bs_init() requires us to fill out the structure + * spdk_bs_dev with a set of callbacks. These callbacks + * implement read, write, and other operations on the + * underlying disks. As a convenience, a utility function + * is provided that creates an spdk_bs_dev that implements + * all of the callbacks by forwarding the I/O to the + * SPDK bdev layer. Other helper functions are also + * available in the blob lib in blob_bdev.c that simply + * make it easier to layer blobstore on top of a bdev. + * However blobstore can be more tightly integrated into + * any lower layer, such as NVMe for example. + */ + bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); + if (bs_dev == NULL) { + SPDK_ERRLOG("Could not create blob bdev!!\n"); + spdk_app_stop(-1); + return; + } + + spdk_bs_init(bs_dev, NULL, bs_init_complete, hello_context); +} + +int +main(int argc, char **argv) +{ + struct spdk_app_opts opts = {}; + int rc = 0; + struct hello_context_t *hello_context = NULL; + + SPDK_NOTICELOG("entry\n"); + + /* Set default values in opts structure. */ + spdk_app_opts_init(&opts); + + /* + * Setup a few specifics before we init, for most SPDK cmd line + * apps, the config file will be passed in as an arg but to make + * this example super simple we just hardcode it. We also need to + * specify a name for the app. + */ + opts.name = "hello_blob"; + opts.config_file = "hello_blob.conf"; + + + /* + * Now we'll allocate and intialize the blobstore itself. We + * can pass in an spdk_bs_opts if we want something other than + * the defaults (cluster size, etc), but here we'll just take the + * defaults. We'll also pass in a struct that we'll use for + * callbacks so we've got efficient bookeeping of what we're + * creating. This is an async operation and bs_init_complete() + * will be called when it is complete. + */ + hello_context = calloc(1, sizeof(struct hello_context_t)); + if (hello_context != NULL) { + /* + * spdk_app_start() will block running hello_start() until + * spdk_app_stop() is called by someone (not simply when + * hello_start() returns), or if an error occurs during + * spdk_app_start() before hello_start() runs. + */ + rc = spdk_app_start(&opts, hello_start, hello_context, NULL); + if (rc) { + SPDK_NOTICELOG("ERROR!\n"); + } else { + SPDK_NOTICELOG("SUCCCESS!\n"); + } + /* Free up memory that we allocated */ + hello_cleanup(hello_context); + } else { + SPDK_ERRLOG("Could not alloc hello_context struct!!\n"); + rc = -ENOMEM; + } + + /* Gracefully close out all of the SPDK subsystems. */ + spdk_app_fini(); + return rc; +} diff --git a/src/spdk/examples/blob/hello_world/hello_blob.conf b/src/spdk/examples/blob/hello_world/hello_blob.conf new file mode 100644 index 00000000..3fa7e9d9 --- /dev/null +++ b/src/spdk/examples/blob/hello_world/hello_blob.conf @@ -0,0 +1,3 @@ +[Malloc] + NumberOfLuns 1 + LunSizeInMB 16 |