blob: add metadata dump capability

Add spdk_bs_dump which dumps low level blobstore metadata
information to a specified FILE.

Also add a corresponding -D option to blobcli which
utilizes this new functionality.

Signed-off-by: Jim Harris <james.r.harris@intel.com>
Change-Id: Iad018b70f8caa4f950d55dd308b9000d55d885ae

Reviewed-on: https://review.gerrithub.io/414479
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
This commit is contained in:
Jim Harris 2018-06-07 06:23:49 -07:00
parent a83f91c29a
commit d1d22046df
3 changed files with 360 additions and 1 deletions

View File

@ -41,6 +41,7 @@
#include "spdk/log.h"
#include "spdk/version.h"
#include "spdk/string.h"
#include "spdk/uuid.h"
/*
* The following is not a public header file, but the CLI does expose
@ -80,6 +81,7 @@ enum cli_action_type {
CLI_LIST_BDEVS,
CLI_LIST_BLOBS,
CLI_INIT_BS,
CLI_DUMP_BS,
CLI_SHELL_EXIT,
CLI_HELP,
};
@ -147,6 +149,7 @@ print_cmds(void)
printf("\nCommands include:\n");
printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n");
printf("\t-d <blobid> filename - dump contents of a blob to a file\n");
printf("\t-D - dump metadata contents of an existing blobstore\n");
printf("\t-f <blobid> value - fill a blob with a decimal value\n");
printf("\t-h - this help screen\n");
printf("\t-i - initialize a blobstore\n");
@ -958,6 +961,77 @@ init_bs(struct cli_context_t *cli_context)
cli_context);
}
static void
spdk_bsdump_done(void *arg, int bserrno)
{
struct cli_context_t *cli_context = arg;
if (cli_context->cli_mode == CLI_MODE_CMD) {
spdk_app_stop(0);
} else {
cli_context->action = CLI_NONE;
cli_start(cli_context, NULL);
}
}
static void
bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value,
size_t value_len)
{
if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) {
if (strcmp(name, "name") == 0) {
fprintf(fp, "%.*s", (int)value_len, (char *)value);
} else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) {
uint64_t length;
memcpy(&length, value, sizeof(length));
fprintf(fp, "%" PRIu64, length);
} else {
fprintf(fp, "?");
}
} else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) {
if (strcmp(name, "name") == 0) {
fprintf(fp, "%s", (char *)value);
} else if (strcmp(name, "uuid") == 0 && value_len == sizeof(struct spdk_uuid)) {
char uuid[SPDK_UUID_STRING_LEN];
spdk_uuid_fmt_lower(uuid, sizeof(uuid), (struct spdk_uuid *)value);
fprintf(fp, "%s", uuid);
} else {
fprintf(fp, "?");
}
} else {
fprintf(fp, "?");
}
}
/*
* Dump metadata of an existing blobstore in a human-readable format.
*/
static void
dump_bs(struct cli_context_t *cli_context)
{
struct spdk_bdev *bdev = NULL;
bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
if (bdev == NULL) {
printf("Could not find a bdev\n");
spdk_app_stop(-1);
return;
}
printf("Init blobstore using bdev Product Name: %s\n",
spdk_bdev_get_product_name(bdev));
cli_context->bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
if (cli_context->bs_dev == NULL) {
printf("Could not create blob bdev!!\n");
spdk_app_stop(-1);
return;
}
spdk_bs_dump(cli_context->bs_dev, stdout, bsdump_print_xattr, spdk_bsdump_done, cli_context);
}
/*
* Common cmd/option parser for command and shell modes.
*/
@ -968,7 +1042,7 @@ cmd_parser(int argc, char **argv, struct cli_context_t *cli_context)
int cmd_chosen = 0;
char resp;
while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:ST:Xx:")) != -1) {
while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:DST:Xx:")) != -1) {
switch (op) {
case 'b':
if (strcmp(cli_context->bdev_name, "") == 0) {
@ -985,6 +1059,10 @@ cmd_parser(int argc, char **argv, struct cli_context_t *cli_context)
usage(cli_context, "ERROR: -c option not valid during shell mode.\n");
}
break;
case 'D':
cmd_chosen++;
cli_context->action = CLI_DUMP_BS;
break;
case 'd':
if (argv[optind] != NULL) {
cmd_chosen++;
@ -1381,6 +1459,9 @@ cli_start(void *arg1, void *arg2)
case CLI_INIT_BS:
init_bs(cli_context);
break;
case CLI_DUMP_BS:
dump_bs(cli_context);
break;
case CLI_LIST_BDEVS:
list_bdevs(cli_context);
break;

View File

@ -238,6 +238,20 @@ void spdk_bs_load(struct spdk_bs_dev *dev, struct spdk_bs_opts *opts,
void spdk_bs_init(struct spdk_bs_dev *dev, struct spdk_bs_opts *opts,
spdk_bs_op_with_handle_complete cb_fn, void *cb_arg);
typedef void (*spdk_bs_dump_print_xattr)(FILE *fp, const char *bstype, const char *name,
const void *value, size_t value_length);
/**
* Dump a blobstore's metadata to a given FILE in human-readable format.
*
* \param dev Blobstore block device.
* \param fp FILE pointer to dump the metadata contents.
* \param print_xattr_fn Callback function to interpret external xattrs.
* \param cb_fn Called when the dump is complete.
* \param cb_arg Argument passed to function cb_fn.
*/
void spdk_bs_dump(struct spdk_bs_dev *dev, FILE *fp, spdk_bs_dump_print_xattr print_xattr_fn,
spdk_bs_op_complete cb_fn, void *cb_arg);
/**
* Destroy the blobstore.
*

View File

@ -3194,6 +3194,270 @@ spdk_bs_load(struct spdk_bs_dev *dev, struct spdk_bs_opts *o,
/* END spdk_bs_load */
/* START spdk_bs_dump */
struct spdk_bs_dump_ctx {
struct spdk_blob_store *bs;
struct spdk_bs_super_block *super;
uint32_t cur_page;
struct spdk_blob_md_page *page;
spdk_bs_sequence_t *seq;
FILE *fp;
spdk_bs_dump_print_xattr print_xattr_fn;
char xattr_name[4096];
};
static void
_spdk_bs_dump_finish(spdk_bs_sequence_t *seq, struct spdk_bs_dump_ctx *ctx, int bserrno)
{
spdk_dma_free(ctx->super);
/*
* We need to defer calling spdk_bs_call_cpl() until after
* dev destuction, so tuck these away for later use.
*/
ctx->bs->unload_err = bserrno;
memcpy(&ctx->bs->unload_cpl, &seq->cpl, sizeof(struct spdk_bs_cpl));
seq->cpl.type = SPDK_BS_CPL_TYPE_NONE;
spdk_bs_sequence_finish(seq, 0);
_spdk_bs_free(ctx->bs);
free(ctx);
}
static void _spdk_bs_dump_read_md_page(spdk_bs_sequence_t *seq, void *cb_arg);
static void
_spdk_bs_dump_print_md_page(struct spdk_bs_dump_ctx *ctx)
{
uint32_t page_idx = ctx->cur_page;
struct spdk_blob_md_page *page = ctx->page;
struct spdk_blob_md_descriptor *desc;
size_t cur_desc = 0;
uint32_t crc;
fprintf(ctx->fp, "=========\n");
fprintf(ctx->fp, "Metadata Page Index: %" PRIu32 " (0x%" PRIx32 ")\n", page_idx, page_idx);
fprintf(ctx->fp, "Blob ID: 0x%" PRIx64 "\n", page->id);
crc = _spdk_blob_md_page_calc_crc(page);
fprintf(ctx->fp, "CRC: 0x%" PRIx32 " (%s)\n", page->crc, crc == page->crc ? "OK" : "Mismatch");
desc = (struct spdk_blob_md_descriptor *)page->descriptors;
while (cur_desc < sizeof(page->descriptors)) {
if (desc->type == SPDK_MD_DESCRIPTOR_TYPE_PADDING) {
if (desc->length == 0) {
/* If padding and length are 0, this terminates the page */
break;
}
} else if (desc->type == SPDK_MD_DESCRIPTOR_TYPE_EXTENT) {
struct spdk_blob_md_descriptor_extent *desc_extent;
unsigned int i;
desc_extent = (struct spdk_blob_md_descriptor_extent *)desc;
for (i = 0; i < desc_extent->length / sizeof(desc_extent->extents[0]); i++) {
if (desc_extent->extents[i].cluster_idx != 0) {
fprintf(ctx->fp, "Allocated Extent - Start: %" PRIu32,
desc_extent->extents[i].cluster_idx);
} else {
fprintf(ctx->fp, "Unallocated Extent - ");
}
fprintf(ctx->fp, " Length: %" PRIu32, desc_extent->extents[i].length);
fprintf(ctx->fp, "\n");
}
} else if (desc->type == SPDK_MD_DESCRIPTOR_TYPE_XATTR) {
struct spdk_blob_md_descriptor_xattr *desc_xattr;
uint32_t i;
desc_xattr = (struct spdk_blob_md_descriptor_xattr *)desc;
if (desc_xattr->length !=
sizeof(desc_xattr->name_length) + sizeof(desc_xattr->value_length) +
desc_xattr->name_length + desc_xattr->value_length) {
}
memcpy(ctx->xattr_name, desc_xattr->name, desc_xattr->name_length);
ctx->xattr_name[desc_xattr->name_length] = '\0';
fprintf(ctx->fp, "XATTR: name = \"%s\"\n", ctx->xattr_name);
fprintf(ctx->fp, " value = \"");
ctx->print_xattr_fn(ctx->fp, ctx->super->bstype.bstype, ctx->xattr_name,
(void *)((uintptr_t)desc_xattr->name + desc_xattr->name_length),
desc_xattr->value_length);
fprintf(ctx->fp, "\"\n");
for (i = 0; i < desc_xattr->value_length; i++) {
if (i % 16 == 0) {
fprintf(ctx->fp, " ");
}
fprintf(ctx->fp, "%02" PRIx8 " ", *((uint8_t *)desc_xattr->name + desc_xattr->name_length + i));
if ((i + 1) % 16 == 0) {
fprintf(ctx->fp, "\n");
}
}
if (i % 16 != 0) {
fprintf(ctx->fp, "\n");
}
} else if (desc->type == SPDK_MD_DESCRIPTOR_TYPE_XATTR_INTERNAL) {
/* TODO */
} else if (desc->type == SPDK_MD_DESCRIPTOR_TYPE_FLAGS) {
/* TODO */
} else {
/* Error */
}
/* Advance to the next descriptor */
cur_desc += sizeof(*desc) + desc->length;
if (cur_desc + sizeof(*desc) > sizeof(page->descriptors)) {
break;
}
desc = (struct spdk_blob_md_descriptor *)((uintptr_t)page->descriptors + cur_desc);
}
}
static void
_spdk_bs_dump_read_md_page_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno)
{
struct spdk_bs_dump_ctx *ctx = cb_arg;
if (bserrno != 0) {
_spdk_bs_dump_finish(seq, ctx, bserrno);
return;
}
if (ctx->page->id != 0) {
_spdk_bs_dump_print_md_page(ctx);
}
ctx->cur_page++;
if (ctx->cur_page < ctx->super->md_len) {
_spdk_bs_dump_read_md_page(seq, cb_arg);
} else {
spdk_dma_free(ctx->page);
_spdk_bs_dump_finish(seq, ctx, 0);
}
}
static void
_spdk_bs_dump_read_md_page(spdk_bs_sequence_t *seq, void *cb_arg)
{
struct spdk_bs_dump_ctx *ctx = cb_arg;
uint64_t lba;
assert(ctx->cur_page < ctx->super->md_len);
lba = _spdk_bs_page_to_lba(ctx->bs, ctx->super->md_start + ctx->cur_page);
spdk_bs_sequence_read_dev(seq, ctx->page, lba,
_spdk_bs_byte_to_lba(ctx->bs, SPDK_BS_PAGE_SIZE),
_spdk_bs_dump_read_md_page_cpl, ctx);
}
static void
_spdk_bs_dump_super_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno)
{
struct spdk_bs_dump_ctx *ctx = cb_arg;
fprintf(ctx->fp, "Signature: \"%.8s\" ", ctx->super->signature);
if (memcmp(ctx->super->signature, SPDK_BS_SUPER_BLOCK_SIG,
sizeof(ctx->super->signature)) != 0) {
fprintf(ctx->fp, "(Mismatch)\n");
_spdk_bs_dump_finish(seq, ctx, bserrno);
return;
} else {
fprintf(ctx->fp, "(OK)\n");
}
fprintf(ctx->fp, "Version: %" PRIu32 "\n", ctx->super->version);
fprintf(ctx->fp, "CRC: 0x%x (%s)\n", ctx->super->crc,
(ctx->super->crc == _spdk_blob_md_page_calc_crc(ctx->super)) ? "OK" : "Mismatch");
fprintf(ctx->fp, "Blobstore Type: %.*s\n", SPDK_BLOBSTORE_TYPE_LENGTH, ctx->super->bstype.bstype);
fprintf(ctx->fp, "Cluster Size: %" PRIu32 "\n", ctx->super->cluster_size);
fprintf(ctx->fp, "Super Blob ID: ");
if (ctx->super->super_blob == SPDK_BLOBID_INVALID) {
fprintf(ctx->fp, "(None)\n");
} else {
fprintf(ctx->fp, "%" PRIu64 "\n", ctx->super->super_blob);
}
fprintf(ctx->fp, "Clean: %" PRIu32 "\n", ctx->super->clean);
fprintf(ctx->fp, "Used Metadata Page Mask Start: %" PRIu32 "\n", ctx->super->used_page_mask_start);
fprintf(ctx->fp, "Used Metadata Page Mask Length: %" PRIu32 "\n", ctx->super->used_page_mask_len);
fprintf(ctx->fp, "Used Cluster Mask Start: %" PRIu32 "\n", ctx->super->used_cluster_mask_start);
fprintf(ctx->fp, "Used Cluster Mask Length: %" PRIu32 "\n", ctx->super->used_cluster_mask_len);
fprintf(ctx->fp, "Used Blob ID Mask Start: %" PRIu32 "\n", ctx->super->used_blobid_mask_start);
fprintf(ctx->fp, "Used Blob ID Mask Length: %" PRIu32 "\n", ctx->super->used_blobid_mask_len);
fprintf(ctx->fp, "Metadata Start: %" PRIu32 "\n", ctx->super->md_start);
fprintf(ctx->fp, "Metadata Length: %" PRIu32 "\n", ctx->super->md_len);
ctx->cur_page = 0;
ctx->page = spdk_dma_zmalloc(SPDK_BS_PAGE_SIZE,
SPDK_BS_PAGE_SIZE,
NULL);
if (!ctx->page) {
_spdk_bs_dump_finish(seq, ctx, -ENOMEM);
return;
}
_spdk_bs_dump_read_md_page(seq, cb_arg);
}
void
spdk_bs_dump(struct spdk_bs_dev *dev, FILE *fp, spdk_bs_dump_print_xattr print_xattr_fn,
spdk_bs_op_complete cb_fn, void *cb_arg)
{
struct spdk_blob_store *bs;
struct spdk_bs_cpl cpl;
spdk_bs_sequence_t *seq;
struct spdk_bs_dump_ctx *ctx;
struct spdk_bs_opts opts = {};
SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Dumping blobstore from dev %p\n", dev);
spdk_bs_opts_init(&opts);
bs = _spdk_bs_alloc(dev, &opts);
if (!bs) {
dev->destroy(dev);
cb_fn(cb_arg, -ENOMEM);
return;
}
ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
_spdk_bs_free(bs);
cb_fn(cb_arg, -ENOMEM);
return;
}
ctx->bs = bs;
ctx->fp = fp;
ctx->print_xattr_fn = print_xattr_fn;
/* Allocate memory for the super block */
ctx->super = spdk_dma_zmalloc(sizeof(*ctx->super), 0x1000, NULL);
if (!ctx->super) {
free(ctx);
_spdk_bs_free(bs);
cb_fn(cb_arg, -ENOMEM);
return;
}
cpl.type = SPDK_BS_CPL_TYPE_BS_BASIC;
cpl.u.bs_basic.cb_fn = cb_fn;
cpl.u.bs_basic.cb_arg = cb_arg;
seq = spdk_bs_sequence_start(bs->md_channel, &cpl);
if (!seq) {
spdk_dma_free(ctx->super);
free(ctx);
_spdk_bs_free(bs);
cb_fn(cb_arg, -ENOMEM);
return;
}
/* Read the super block */
spdk_bs_sequence_read_dev(seq, ctx->super, _spdk_bs_page_to_lba(bs, 0),
_spdk_bs_byte_to_lba(bs, sizeof(*ctx->super)),
_spdk_bs_dump_super_cpl, ctx);
}
/* END spdk_bs_dump */
/* START spdk_bs_init */
struct spdk_bs_init_ctx {