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:
parent
a83f91c29a
commit
d1d22046df
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user