Commit 32b4ab71 authored by Cunyin Chang's avatar Cunyin Chang Committed by Jim Harris
Browse files

bdev/error: Add new bdev/error to inject errors in bdev layer.



Change-Id: I3c68cb5911b3662dbb35f551dc291d5ebdd84bf6
Signed-off-by: default avatarCunyin Chang <cunyin.chang@intel.com>
parent 679e2831
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ CFLAGS += $(ENV_CFLAGS) -I.
C_SRCS = bdev.c scsi_nvme.c
LIBNAME = bdev

DIRS-y += malloc null nvme rpc split
DIRS-y += error malloc null nvme rpc split

ifeq ($(OS),Linux)
DIRS-y += aio
+41 −0
Original line number Diff line number Diff line
#
#  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.
#

SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk

CFLAGS += $(ENV_CFLAGS) -I$(SPDK_ROOT_DIR)/lib/bdev/
C_SRCS = vbdev_error.c vbdev_error_rpc.c
LIBNAME = vbdev_error

include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
+285 −0
Original line number Diff line number Diff line
/*-
 *   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.
 */

/*
 * This is a module for test purpose which will simulate error cases for bdev.
 */

#include "spdk/stdinc.h"
#include "spdk/rpc.h"
#include "spdk/conf.h"
#include "spdk/endian.h"
#include "spdk/nvme_spec.h"

#include "spdk_internal/bdev.h"
#include "spdk_internal/log.h"

#include "vbdev_error.h"

/* Context for each error bdev */
struct vbdev_error_disk {
	struct spdk_bdev		disk;
	struct spdk_bdev		*base_bdev;
	TAILQ_ENTRY(vbdev_error_disk)	tailq;
};

static uint32_t g_io_type_mask;
static uint32_t g_error_num;
static pthread_mutex_t g_vbdev_error_mutex = PTHREAD_MUTEX_INITIALIZER;
static TAILQ_HEAD(, vbdev_error_disk) g_vbdev_error_disks = TAILQ_HEAD_INITIALIZER(
			g_vbdev_error_disks);

void
spdk_vbdev_inject_error(uint32_t io_type_mask, uint32_t error_num)
{
	pthread_mutex_lock(&g_vbdev_error_mutex);
	g_io_type_mask = io_type_mask;
	g_error_num = error_num;
	pthread_mutex_unlock(&g_vbdev_error_mutex);
}

static void
vbdev_error_task_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status status,
			  void *cb_arg)
{
	struct spdk_bdev_io *bdevio = (struct spdk_bdev_io *)cb_arg;
	struct spdk_bdev_io *parent = bdevio->parent;

	spdk_bdev_io_complete(parent, SPDK_BDEV_IO_STATUS_FAILED);
}

static void
vbdev_error_reset(struct vbdev_error_disk *error_disk, struct spdk_bdev_io *bdev_io)
{
	/*
	 * pass the I/O through unmodified.
	 *
	 * However, we do need to increment the generation count for the error bdev,
	 * since the spdk_bdev_io_complete() path that normally updates it will not execute
	 * after we resubmit the I/O to the base_bdev.
	 */
	if (bdev_io->u.reset.type == SPDK_BDEV_RESET_HARD) {
		error_disk->disk.gencnt++;
	}
}

static void
vbdev_error_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
{
	struct vbdev_error_disk *error_disk = bdev_io->bdev->ctxt;
	uint32_t io_type_mask;
	struct spdk_bdev_io *child;

	switch (bdev_io->type) {
	case SPDK_BDEV_IO_TYPE_READ:
	case SPDK_BDEV_IO_TYPE_WRITE:
	case SPDK_BDEV_IO_TYPE_UNMAP:
	case SPDK_BDEV_IO_TYPE_FLUSH:
		break;
	case SPDK_BDEV_IO_TYPE_RESET:
		vbdev_error_reset(error_disk, bdev_io);
		break;
	default:
		SPDK_ERRLOG("Error Injection: unknown I/O type %d\n", bdev_io->type);
		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
		return;
	}

	io_type_mask = 1U << bdev_io->type;

	if (g_error_num == 0 || !(g_io_type_mask & io_type_mask)) {
		spdk_bdev_io_resubmit(bdev_io, error_disk->base_bdev);
		return;
	}

	pthread_mutex_lock(&g_vbdev_error_mutex);
	/* check again to make sure g_error_num has not been decremented since we checked it above */
	if (g_error_num == 0) {
		spdk_bdev_io_resubmit(bdev_io, error_disk->base_bdev);
	} else {
		g_error_num--;
		child = spdk_bdev_get_child_io(bdev_io, &error_disk->disk, vbdev_error_task_complete, NULL);
		child->ch = bdev_io->ch;
		spdk_bdev_io_resubmit(child, error_disk->base_bdev);
	}
	pthread_mutex_unlock(&g_vbdev_error_mutex);
}

static void
vbdev_error_free(struct vbdev_error_disk *error_disk)
{
	if (!error_disk) {
		return;
	}

	TAILQ_REMOVE(&g_vbdev_error_disks, error_disk, tailq);

	spdk_bdev_unclaim(error_disk->base_bdev);
	free(error_disk);
}

static int
vbdev_error_destruct(void *ctx)
{
	struct vbdev_error_disk *error_disk = ctx;

	vbdev_error_free(error_disk);
	return 0;
}

static bool
vbdev_error_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
{
	struct vbdev_error_disk *error_disk = ctx;

	return error_disk->base_bdev->fn_table->io_type_supported(error_disk->base_bdev,
			io_type);
}

static struct spdk_io_channel *
vbdev_error_get_io_channel(void *ctx, uint32_t priority)
{
	struct vbdev_error_disk *error_disk = ctx;

	return error_disk->base_bdev->fn_table->get_io_channel(error_disk->base_bdev,
			priority);
}

static int
vbdev_error_dump_config_json(void *ctx, struct spdk_json_write_ctx *w)
{
	struct vbdev_error_disk *error_disk = ctx;

	spdk_json_write_name(w, "error_disk");
	spdk_json_write_object_begin(w);

	spdk_json_write_name(w, "base_bdev");
	spdk_json_write_string(w, error_disk->base_bdev->name);

	spdk_json_write_object_end(w);

	return 0;
}

static struct spdk_bdev_fn_table vbdev_error_fn_table = {
	.destruct		= vbdev_error_destruct,
	.io_type_supported	= vbdev_error_io_type_supported,
	.submit_request		= vbdev_error_submit_request,
	.get_io_channel		= vbdev_error_get_io_channel,
	.dump_config_json	= vbdev_error_dump_config_json,
};

int
spdk_vbdev_error_create(struct spdk_bdev *base_bdev)
{
	struct vbdev_error_disk *disk;
	int rc;

	if (!spdk_bdev_claim(base_bdev, NULL, NULL)) {
		SPDK_ERRLOG("Error bdev %s is already claimed\n", base_bdev->name);
		return -1;
	}

	disk = calloc(1, sizeof(*disk));
	if (!disk) {
		SPDK_ERRLOG("Memory allocation failure\n");
		rc = -1;
		goto cleanup;
	}

	disk->base_bdev = base_bdev;
	memcpy(&disk->disk, base_bdev, sizeof(*base_bdev));
	snprintf(disk->disk.name, sizeof(disk->disk.name), "EE_%s", base_bdev->name);
	snprintf(disk->disk.product_name, sizeof(disk->disk.product_name), "Error Injection Disk");
	disk->disk.ctxt = disk;
	disk->disk.fn_table = &vbdev_error_fn_table;

	spdk_bdev_register(&disk->disk);

	TAILQ_INSERT_TAIL(&g_vbdev_error_disks, disk, tailq);

	rc = 0;
	return rc;
cleanup:
	free(disk);
	return rc;
}

static int
vbdev_error_init(void)
{
	struct spdk_conf_section *sp;
	const char *base_bdev_name;
	int i;
	struct spdk_bdev *base_bdev;

	sp = spdk_conf_find_section(NULL, "BdevError");
	if (sp == NULL) {
		return 0;
	}

	for (i = 0; ; i++) {
		if (!spdk_conf_section_get_nval(sp, "BdevError", i)) {
			break;
		}

		base_bdev_name = spdk_conf_section_get_nmval(sp, "BdevError", i, 0);
		if (!base_bdev_name) {
			SPDK_ERRLOG("ErrorInjection configuration missing blockdev name\n");
			return -1;
		}

		base_bdev = spdk_bdev_get_by_name(base_bdev_name);
		if (!base_bdev) {
			SPDK_ERRLOG("Could not find ErrorInjection bdev %s\n", base_bdev_name);
			return -1;
		}

		if (spdk_vbdev_error_create(base_bdev)) {
			return -1;
		}
	}
	return 0;
}

static void
vbdev_error_fini(void)
{
	struct vbdev_error_disk *error_disk, *tmp;

	TAILQ_FOREACH_SAFE(error_disk, &g_vbdev_error_disks, tailq, tmp) {
		vbdev_error_free(error_disk);
	}
}

SPDK_VBDEV_MODULE_REGISTER(vbdev_error_init, vbdev_error_fini, NULL, NULL)
+43 −0
Original line number Diff line number Diff line
/*-
 *   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.
 */

#ifndef SPDK_BLOCKDEV_ERROR_H
#define SPDK_BLOCKDEV_ERROR_H

#include "spdk/stdinc.h"
#include "spdk/bdev.h"

int spdk_vbdev_error_create(struct spdk_bdev *base_bdev);
void spdk_vbdev_inject_error(uint32_t io_type_mask, uint32_t error_num);

#endif // SPDK_BLOCKDEV_ERROR_H
+178 −0
Original line number Diff line number Diff line
/*-
 *   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/string.h"
#include "spdk/rpc.h"
#include "spdk/util.h"

#include "spdk_internal/log.h"
#include "vbdev_error.h"

#define ERROR_BDEV_IO_TYPE_INVALID (1U << (SPDK_BDEV_IO_TYPE_RESET + 1))

static uint32_t
spdk_rpc_error_bdev_io_type_parse(char *name)
{
	if (strcmp(name, "read") == 0) {
		return 1U << SPDK_BDEV_IO_TYPE_READ;
	} else if (strcmp(name, "write") == 0) {
		return 1U << SPDK_BDEV_IO_TYPE_WRITE;
	} else if (strcmp(name, "flush") == 0) {
		return 1U << SPDK_BDEV_IO_TYPE_FLUSH;
	} else if (strcmp(name, "unmap") == 0) {
		return 1U << SPDK_BDEV_IO_TYPE_UNMAP;
	} else if (strcmp(name, "reset") == 0) {
		return 1U << SPDK_BDEV_IO_TYPE_RESET;
	} else if (strcmp(name, "all") == 0) {
		return 0xffffffff;
	} else if (strcmp(name, "clear") == 0) {
		return 0;
	}
	return ERROR_BDEV_IO_TYPE_INVALID;
}

struct rpc_construct_error_bdev {
	char *base_name;
};

static void
free_rpc_construct_error_bdev(struct rpc_construct_error_bdev *req)
{
	free(req->base_name);
}

static const struct spdk_json_object_decoder rpc_construct_error_bdev_decoders[] = {
	{"base_name", offsetof(struct rpc_construct_error_bdev, base_name), spdk_json_decode_string},
};

static void
spdk_rpc_construct_error_bdev(struct spdk_jsonrpc_server_conn *conn,
			      const struct spdk_json_val *params,
			      const struct spdk_json_val *id)
{
	struct rpc_construct_error_bdev req = {};
	struct spdk_json_write_ctx *w;
	struct spdk_bdev *base_bdev;

	if (spdk_json_decode_object(params, rpc_construct_error_bdev_decoders,
				    SPDK_COUNTOF(rpc_construct_error_bdev_decoders),
				    &req)) {
		SPDK_ERRLOG("spdk_json_decode_object failed\n");
		goto invalid;
	}

	base_bdev = spdk_bdev_get_by_name(req.base_name);
	if (!base_bdev) {
		SPDK_ERRLOG("Could not find ErrorInjection bdev %s\n", req.base_name);
		goto invalid;
	}

	if (spdk_vbdev_error_create(base_bdev)) {
		SPDK_ERRLOG("Could not create ErrorInjection bdev %s\n", req.base_name);
		goto invalid;
	}

	if (id == NULL) {
		free_rpc_construct_error_bdev(&req);
		return;
	}

	w = spdk_jsonrpc_begin_result(conn, id);
	spdk_json_write_bool(w, true);
	spdk_jsonrpc_end_result(conn, w);

	free_rpc_construct_error_bdev(&req);

	return;

invalid:
	spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
	free_rpc_construct_error_bdev(&req);
}
SPDK_RPC_REGISTER("construct_error_bdev", spdk_rpc_construct_error_bdev)

struct rpc_error_information {
	char *type;
	uint32_t num;
};

static const struct spdk_json_object_decoder rpc_error_information_decoders[] = {
	{"type", offsetof(struct rpc_error_information, type), spdk_json_decode_string},
	{"num", offsetof(struct rpc_error_information, num), spdk_json_decode_uint32, true},
};

static void
free_rpc_error_information(struct rpc_error_information *p)
{
	free(p->type);
}

static void
spdk_rpc_bdev_inject_error(struct spdk_jsonrpc_server_conn *conn,
			   const struct spdk_json_val *params,
			   const struct spdk_json_val *id)
{
	struct rpc_error_information req = {};
	struct spdk_json_write_ctx *w;
	uint32_t ret;

	if (spdk_json_decode_object(params, rpc_error_information_decoders,
				    SPDK_COUNTOF(rpc_error_information_decoders),
				    &req)) {
		SPDK_ERRLOG("spdk_json_decode_object failed\n");
		goto invalid;
	}

	ret = spdk_rpc_error_bdev_io_type_parse(req.type);
	if (ret == ERROR_BDEV_IO_TYPE_INVALID) {
		goto invalid;
	}

	spdk_vbdev_inject_error(ret, req.num);
	free_rpc_error_information(&req);

	if (id == NULL) {
		return;
	}

	w = spdk_jsonrpc_begin_result(conn, id);
	spdk_json_write_bool(w, true);
	spdk_jsonrpc_end_result(conn, w);
	return;

invalid:
	spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
	free_rpc_error_information(&req);
}
SPDK_RPC_REGISTER("bdev_inject_error", spdk_rpc_bdev_inject_error)
Loading