From patchwork Fri Apr 23 15:30:38 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 50826 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D11B1B7D20 for ; Sat, 24 Apr 2010 01:44:00 +1000 (EST) Received: from localhost ([127.0.0.1]:53539 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1O5L2e-00034b-N4 for incoming@patchwork.ozlabs.org; Fri, 23 Apr 2010 11:43:44 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1O5Kr5-0007K9-3X for qemu-devel@nongnu.org; Fri, 23 Apr 2010 11:31:47 -0400 Received: from [140.186.70.92] (port=50258 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1O5Kr3-0007JJ-CM for qemu-devel@nongnu.org; Fri, 23 Apr 2010 11:31:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1O5Kr1-0005pp-2B for qemu-devel@nongnu.org; Fri, 23 Apr 2010 11:31:45 -0400 Received: from mx1.redhat.com ([209.132.183.28]:62952) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1O5Kr0-0005pf-MR for qemu-devel@nongnu.org; Fri, 23 Apr 2010 11:31:43 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o3NFVeIB031601 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 23 Apr 2010 11:31:40 -0400 Received: from localhost.localdomain (vpn2-9-155.ams2.redhat.com [10.36.9.155]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o3NFVOCB020467; Fri, 23 Apr 2010 11:31:37 -0400 From: Kevin Wolf To: aliguori@linux.vnet.ibm.com Date: Fri, 23 Apr 2010 17:30:38 +0200 Message-Id: <1272036658-26776-7-git-send-email-kwolf@redhat.com> In-Reply-To: <1272036658-26776-1-git-send-email-kwolf@redhat.com> References: <1272036658-26776-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH 06/26] blkdebug: Add events and rules X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Block drivers can trigger a blkdebug event whenever they reach a place where it could be useful to inject an error for testing/debugging purposes. Rules are read from a blkdebug config file and describe which action is taken when an event is triggered. For now this is only injecting an error (with a few options) or changing the state (which is an integer). Rules can be declared to be active only in a specific state; this way later rules can distiguish on which path we came to trigger their event. Signed-off-by: Kevin Wolf --- block.c | 12 +++ block.h | 9 ++ block/blkdebug.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++- block_int.h | 2 + 4 files changed, 272 insertions(+), 1 deletions(-) diff --git a/block.c b/block.c index 0881c93..9351aa3 100644 --- a/block.c +++ b/block.c @@ -1535,6 +1535,18 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, return drv->bdrv_load_vmstate(bs, buf, pos, size); } +void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) +{ + BlockDriver *drv = bs->drv; + + if (!drv || !drv->bdrv_debug_event) { + return; + } + + return drv->bdrv_debug_event(bs, event); + +} + /**************************************************************/ /* handling of snapshots */ diff --git a/block.h b/block.h index edf5704..2fd8361 100644 --- a/block.h +++ b/block.h @@ -207,4 +207,13 @@ int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); int64_t bdrv_get_dirty_count(BlockDriverState *bs); + + +typedef enum { + BLKDBG_EVENT_MAX, +} BlkDebugEvent; + +#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) +void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); + #endif diff --git a/block/blkdebug.c b/block/blkdebug.c index dad3ac6..b813bfa 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -46,6 +46,7 @@ typedef struct BlkdebugVars { typedef struct BDRVBlkdebugState { BlockDriverState *hd; BlkdebugVars vars; + QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; } BDRVBlkdebugState; typedef struct BlkdebugAIOCB { @@ -61,16 +62,211 @@ static AIOPool blkdebug_aio_pool = { .cancel = blkdebug_aio_cancel, }; +enum { + ACTION_INJECT_ERROR, + ACTION_SET_STATE, +}; + +typedef struct BlkdebugRule { + BlkDebugEvent event; + int action; + int state; + union { + struct { + int error; + int immediately; + int once; + } inject; + struct { + int new_state; + } set_state; + } options; + QLIST_ENTRY(BlkdebugRule) next; +} BlkdebugRule; + +static QemuOptsList inject_error_opts = { + .name = "inject-error", + .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), + .desc = { + { + .name = "event", + .type = QEMU_OPT_STRING, + }, + { + .name = "state", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "errno", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "once", + .type = QEMU_OPT_BOOL, + }, + { + .name = "immediately", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList set_state_opts = { + .name = "set-state", + .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), + .desc = { + { + .name = "event", + .type = QEMU_OPT_STRING, + }, + { + .name = "state", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "new_state", + .type = QEMU_OPT_NUMBER, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList *config_groups[] = { + &inject_error_opts, + &set_state_opts, + NULL +}; + +static const char *event_names[BLKDBG_EVENT_MAX] = { +}; + +static int get_event_by_name(const char *name, BlkDebugEvent *event) +{ + int i; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + if (!strcmp(event_names[i], name)) { + *event = i; + return 0; + } + } + + return -1; +} + +struct add_rule_data { + BDRVBlkdebugState *s; + int action; +}; + +static int add_rule(QemuOpts *opts, void *opaque) +{ + struct add_rule_data *d = opaque; + BDRVBlkdebugState *s = d->s; + const char* event_name; + BlkDebugEvent event; + struct BlkdebugRule *rule; + + /* Find the right event for the rule */ + event_name = qemu_opt_get(opts, "event"); + if (!event_name || get_event_by_name(event_name, &event) < 0) { + return -1; + } + + /* Set attributes common for all actions */ + rule = qemu_mallocz(sizeof(*rule)); + *rule = (struct BlkdebugRule) { + .event = event, + .action = d->action, + .state = qemu_opt_get_number(opts, "state", 0), + }; + + /* Parse action-specific options */ + switch (d->action) { + case ACTION_INJECT_ERROR: + rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); + rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); + rule->options.inject.immediately = + qemu_opt_get_bool(opts, "immediately", 0); + break; + + case ACTION_SET_STATE: + rule->options.set_state.new_state = + qemu_opt_get_number(opts, "new_state", 0); + break; + }; + + /* Add the rule */ + QLIST_INSERT_HEAD(&s->rules[event], rule, next); + + return 0; +} + +static int read_config(BDRVBlkdebugState *s, const char *filename) +{ + FILE *f; + int ret; + struct add_rule_data d; + + f = fopen(filename, "r"); + if (f == NULL) { + return -errno; + } + + ret = qemu_config_parse(f, config_groups, filename); + if (ret < 0) { + goto fail; + } + + d.s = s; + d.action = ACTION_INJECT_ERROR; + qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0); + + d.action = ACTION_SET_STATE; + qemu_opts_foreach(&set_state_opts, add_rule, &d, 0); + + ret = 0; +fail: + fclose(f); + return ret; +} + +/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags) { BDRVBlkdebugState *s = bs->opaque; + int ret; + char *config, *c; + /* Parse the blkdebug: prefix */ if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) { return -EINVAL; } filename += strlen("blkdebug:"); - return bdrv_file_open(&s->hd, filename, flags); + /* Read rules from config file */ + c = strchr(filename, ':'); + if (c == NULL) { + return -EINVAL; + } + + config = strdup(filename); + config[c - filename] = '\0'; + ret = read_config(s, config); + free(config); + if (ret < 0) { + return ret; + } + filename = c + 1; + + /* Open the backing file */ + ret = bdrv_file_open(&s->hd, filename, flags); + if (ret < 0) { + return ret; + } + + return 0; } static void error_callback_bh(void *opaque) @@ -146,6 +342,16 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs, static void blkdebug_close(BlockDriverState *bs) { BDRVBlkdebugState *s = bs->opaque; + BlkdebugRule *rule, *next; + int i; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { + QLIST_REMOVE(rule, next); + qemu_free(rule); + } + } + bdrv_delete(s->hd); } @@ -162,6 +368,46 @@ static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs, return bdrv_aio_flush(s->hd, cb, opaque); } +static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, + BlkdebugVars *old_vars) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugVars *vars = &s->vars; + + /* Only process rules for the current state */ + if (rule->state && rule->state != old_vars->state) { + return; + } + + /* Take the action */ + switch (rule->action) { + case ACTION_INJECT_ERROR: + vars->inject_errno = rule->options.inject.error; + vars->inject_once = rule->options.inject.once; + vars->inject_immediately = rule->options.inject.immediately; + break; + + case ACTION_SET_STATE: + vars->state = rule->options.set_state.new_state; + break; + } +} + +static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) +{ + BDRVBlkdebugState *s = bs->opaque; + struct BlkdebugRule *rule; + BlkdebugVars old_vars = s->vars; + + if (event < 0 || event >= BLKDBG_EVENT_MAX) { + return; + } + + QLIST_FOREACH(rule, &s->rules[event], next) { + process_rule(bs, rule, &old_vars); + } +} + static BlockDriver bdrv_blkdebug = { .format_name = "blkdebug", .protocol_name = "blkdebug", @@ -175,6 +421,8 @@ static BlockDriver bdrv_blkdebug = { .bdrv_aio_readv = blkdebug_aio_readv, .bdrv_aio_writev = blkdebug_aio_writev, .bdrv_aio_flush = blkdebug_aio_flush, + + .bdrv_debug_event = blkdebug_debug_event, }; static void bdrv_blkdebug_init(void) diff --git a/block_int.h b/block_int.h index 71b633b..e7e1e7e 100644 --- a/block_int.h +++ b/block_int.h @@ -120,6 +120,8 @@ struct BlockDriver { /* Returns number of errors in image, -errno for internal errors */ int (*bdrv_check)(BlockDriverState* bs); + void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); + /* Set if newly created images are not guaranteed to contain only zeros */ int no_zero_init;