Each virtual bdev now has a pointer to its base bdev, and a base bdev has a pointers to any virtual bdevs built on top of it. Also add a new set of leaf iterators, to get only bdevs that have no virtual bdevs built on top of them. These iterators are now used by the bdevio and bdevperf utilities, in advance of the claim/unclaim semantics getting removed in a future patch. Signed-off-by: Jim Harris <james.r.harris@intel.com> Change-Id: I669783764407cdd4920b5ee121959e2a58c8d436 Reviewed-on: https://review.gerrithub.io/367610 Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com> Tested-by: SPDK Automated Test System <sys_sgsw@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com>
307 lines
7.8 KiB
C
307 lines
7.8 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.
|
|
*/
|
|
|
|
/*
|
|
* 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/string.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;
|
|
uint32_t io_type_mask;
|
|
uint32_t error_num;
|
|
TAILQ_ENTRY(vbdev_error_disk) tailq;
|
|
};
|
|
|
|
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);
|
|
|
|
int
|
|
spdk_vbdev_inject_error(char *name, uint32_t io_type_mask, uint32_t error_num)
|
|
{
|
|
struct spdk_bdev *bdev;
|
|
struct vbdev_error_disk *error_disk;
|
|
|
|
pthread_mutex_lock(&g_vbdev_error_mutex);
|
|
bdev = spdk_bdev_get_by_name(name);
|
|
if (!bdev) {
|
|
SPDK_ERRLOG("Could not find ErrorInjection bdev %s\n", name);
|
|
pthread_mutex_unlock(&g_vbdev_error_mutex);
|
|
return -1;
|
|
}
|
|
|
|
TAILQ_FOREACH(error_disk, &g_vbdev_error_disks, tailq) {
|
|
if (bdev == &error_disk->disk) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (error_disk == NULL) {
|
|
SPDK_ERRLOG("Could not find ErrorInjection bdev %s\n", name);
|
|
pthread_mutex_unlock(&g_vbdev_error_mutex);
|
|
return -1;
|
|
}
|
|
|
|
error_disk->io_type_mask = io_type_mask;
|
|
error_disk->error_num = error_num;
|
|
pthread_mutex_unlock(&g_vbdev_error_mutex);
|
|
return 0;
|
|
}
|
|
|
|
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.
|
|
*/
|
|
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;
|
|
|
|
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 (error_disk->error_num == 0 || !(error_disk->io_type_mask & io_type_mask)) {
|
|
spdk_bdev_io_resubmit(bdev_io, error_disk->base_bdev);
|
|
return;
|
|
}
|
|
|
|
error_disk->error_num--;
|
|
spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
}
|
|
|
|
static void
|
|
vbdev_error_disk_free(struct vbdev_error_disk *disk)
|
|
{
|
|
if (!disk) {
|
|
return;
|
|
}
|
|
|
|
free(disk->disk.name);
|
|
free(disk);
|
|
}
|
|
|
|
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);
|
|
vbdev_error_disk_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)
|
|
{
|
|
struct vbdev_error_disk *error_disk = ctx;
|
|
|
|
return error_disk->base_bdev->fn_table->get_io_channel(error_disk->base_bdev);
|
|
}
|
|
|
|
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));
|
|
disk->disk.name = spdk_sprintf_alloc("EE_%s", base_bdev->name);
|
|
if (!disk->disk.name) {
|
|
rc = -ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
disk->disk.product_name = "Error Injection Disk";
|
|
disk->disk.ctxt = disk;
|
|
disk->disk.fn_table = &vbdev_error_fn_table;
|
|
|
|
spdk_vbdev_register(&disk->disk, &base_bdev, 1);
|
|
|
|
TAILQ_INSERT_TAIL(&g_vbdev_error_disks, disk, tailq);
|
|
|
|
rc = 0;
|
|
return rc;
|
|
cleanup:
|
|
vbdev_error_disk_free(disk);
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
vbdev_error_init(void)
|
|
{
|
|
struct spdk_conf_section *sp;
|
|
const char *base_bdev_name;
|
|
int i, rc = 0;
|
|
struct spdk_bdev *base_bdev;
|
|
|
|
sp = spdk_conf_find_section(NULL, "BdevError");
|
|
if (sp == NULL) {
|
|
goto end;
|
|
}
|
|
|
|
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");
|
|
rc = -1;
|
|
goto end;
|
|
}
|
|
|
|
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);
|
|
rc = -1;
|
|
goto end;
|
|
}
|
|
|
|
if (spdk_vbdev_error_create(base_bdev)) {
|
|
rc = -1;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
end:
|
|
spdk_vbdev_module_init_next(rc);
|
|
}
|
|
|
|
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, NULL)
|