Blobstore now supports 512B reads/writes to blobs, if the backing device is formatted for 512B LBAs. The hello_blob example app was never updated to account for this - so when running against a backing device with 512B LBAs, it would fail since it was only reading/writing 1 blob io_unit (512B) but was comparing a page size (4KB). Clean up a typo too while we're here. Signed-off-by: Jim Harris <james.r.harris@intel.com> Change-Id: I6cfeeff1c160a24d4c10b68b9dd93717ed79f212 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/450069 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Paul Luse <paul.e.luse@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
499 lines
13 KiB
C
499 lines
13 KiB
C
/*-
|
|
* 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 io_unit_size;
|
|
int rc;
|
|
};
|
|
|
|
/*
|
|
* Free up memory that we allocated.
|
|
*/
|
|
static void
|
|
hello_cleanup(struct hello_context_t *hello_context)
|
|
{
|
|
spdk_free(hello_context->read_buff);
|
|
spdk_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->io_unit_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_malloc(hello_context->io_unit_size,
|
|
0x1000, NULL, SPDK_ENV_LCORE_ID_ANY,
|
|
SPDK_MALLOC_DMA);
|
|
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
|
|
* transfer 1 io_unit of 4K aligned data at offset 0 in the blob.
|
|
*/
|
|
hello_context->write_buff = spdk_malloc(hello_context->io_unit_size,
|
|
0x1000, NULL, SPDK_ENV_LCORE_ID_ANY,
|
|
SPDK_MALLOC_DMA);
|
|
if (hello_context->write_buff == NULL) {
|
|
unload_bs(hello_context, "Error in allocating memory",
|
|
-ENOMEM);
|
|
return;
|
|
}
|
|
memset(hello_context->write_buff, 0x5a, hello_context->io_unit_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 io_unit 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 io_unit size in allocating buffers, etc., later
|
|
* so we'll just save it in out context buffer here.
|
|
*/
|
|
hello_context->io_unit_size = spdk_bs_get_io_unit_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)
|
|
{
|
|
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);
|
|
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;
|
|
}
|