Jim Harris 1a2de82456 examples/hello_blob: use io_unit_size instead of page_size
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>
2019-04-04 01:10:14 +00:00

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;
}