Commit d1d22046 authored by Jim Harris's avatar Jim Harris
Browse files

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: default avatarJim Harris <james.r.harris@intel.com>
Change-Id: Iad018b70f8caa4f950d55dd308b9000d55d885ae

Reviewed-on: https://review.gerrithub.io/414479


Reviewed-by: default avatarPaul Luse <paul.e.luse@intel.com>
Reviewed-by: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Tested-by: default avatarSPDK Automated Test System <sys_sgsw@intel.com>
parent a83f91c2
Loading
Loading
Loading
Loading
+82 −1
Original line number Diff line number Diff line
@@ -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;
+14 −0
Original line number Diff line number Diff line
@@ -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.
 *
+264 −0
Original line number Diff line number Diff line
@@ -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 {