aboutsummaryrefslogtreecommitdiff
path: root/block/blkdebug.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/blkdebug.c')
-rw-r--r--block/blkdebug.c133
1 files changed, 126 insertions, 7 deletions
diff --git a/block/blkdebug.c b/block/blkdebug.c
index d61ece8..6f74637 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -23,14 +23,17 @@
*/
#include "qemu-common.h"
-#include "block_int.h"
-#include "module.h"
+#include "qemu/config-file.h"
+#include "block/block_int.h"
+#include "qemu/module.h"
typedef struct BDRVBlkdebugState {
int state;
int new_state;
+
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
+ QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
} BDRVBlkdebugState;
typedef struct BlkdebugAIOCB {
@@ -39,6 +42,12 @@ typedef struct BlkdebugAIOCB {
int ret;
} BlkdebugAIOCB;
+typedef struct BlkdebugSuspendedReq {
+ Coroutine *co;
+ char *tag;
+ QLIST_ENTRY(BlkdebugSuspendedReq) next;
+} BlkdebugSuspendedReq;
+
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
static const AIOCBInfo blkdebug_aiocb_info = {
@@ -49,6 +58,7 @@ static const AIOCBInfo blkdebug_aiocb_info = {
enum {
ACTION_INJECT_ERROR,
ACTION_SET_STATE,
+ ACTION_SUSPEND,
};
typedef struct BlkdebugRule {
@@ -65,6 +75,9 @@ typedef struct BlkdebugRule {
struct {
int new_state;
} set_state;
+ struct {
+ char *tag;
+ } suspend;
} options;
QLIST_ENTRY(BlkdebugRule) next;
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
@@ -226,6 +239,11 @@ static int add_rule(QemuOpts *opts, void *opaque)
rule->options.set_state.new_state =
qemu_opt_get_number(opts, "new_state", 0);
break;
+
+ case ACTION_SUSPEND:
+ rule->options.suspend.tag =
+ g_strdup(qemu_opt_get(opts, "tag"));
+ break;
};
/* Add the rule */
@@ -234,12 +252,32 @@ static int add_rule(QemuOpts *opts, void *opaque)
return 0;
}
+static void remove_rule(BlkdebugRule *rule)
+{
+ switch (rule->action) {
+ case ACTION_INJECT_ERROR:
+ case ACTION_SET_STATE:
+ break;
+ case ACTION_SUSPEND:
+ g_free(rule->options.suspend.tag);
+ break;
+ }
+
+ QLIST_REMOVE(rule, next);
+ g_free(rule);
+}
+
static int read_config(BDRVBlkdebugState *s, const char *filename)
{
FILE *f;
int ret;
struct add_rule_data d;
+ /* Allow usage without config file */
+ if (!*filename) {
+ return 0;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
return -errno;
@@ -389,6 +427,7 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
}
+
static void blkdebug_close(BlockDriverState *bs)
{
BDRVBlkdebugState *s = bs->opaque;
@@ -397,12 +436,32 @@ static void blkdebug_close(BlockDriverState *bs)
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
- QLIST_REMOVE(rule, next);
- g_free(rule);
+ remove_rule(rule);
}
}
}
+static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ BlkdebugSuspendedReq r;
+
+ r = (BlkdebugSuspendedReq) {
+ .co = qemu_coroutine_self(),
+ .tag = g_strdup(rule->options.suspend.tag),
+ };
+
+ remove_rule(rule);
+ QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
+
+ printf("blkdebug: Suspended request '%s'\n", r.tag);
+ qemu_coroutine_yield();
+ printf("blkdebug: Resuming request '%s'\n", r.tag);
+
+ QLIST_REMOVE(&r, next);
+ g_free(r.tag);
+}
+
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
bool injected)
{
@@ -426,6 +485,10 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
case ACTION_SET_STATE:
s->new_state = rule->options.set_state.new_state;
break;
+
+ case ACTION_SUSPEND:
+ suspend_request(bs, rule);
+ break;
}
return injected;
}
@@ -433,19 +496,72 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
{
BDRVBlkdebugState *s = bs->opaque;
- struct BlkdebugRule *rule;
+ struct BlkdebugRule *rule, *next;
bool injected;
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
injected = false;
s->new_state = s->state;
- QLIST_FOREACH(rule, &s->rules[event], next) {
+ QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
injected = process_rule(bs, rule, injected);
}
s->state = s->new_state;
}
+static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
+ const char *tag)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ struct BlkdebugRule *rule;
+ BlkDebugEvent blkdebug_event;
+
+ if (get_event_by_name(event, &blkdebug_event) < 0) {
+ return -ENOENT;
+ }
+
+
+ rule = g_malloc(sizeof(*rule));
+ *rule = (struct BlkdebugRule) {
+ .event = blkdebug_event,
+ .action = ACTION_SUSPEND,
+ .state = 0,
+ .options.suspend.tag = g_strdup(tag),
+ };
+
+ QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
+
+ return 0;
+}
+
+static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ BlkdebugSuspendedReq *r;
+
+ QLIST_FOREACH(r, &s->suspended_reqs, next) {
+ if (!strcmp(r->tag, tag)) {
+ qemu_coroutine_enter(r->co, NULL);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+
+static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ BlkdebugSuspendedReq *r;
+
+ QLIST_FOREACH(r, &s->suspended_reqs, next) {
+ if (!strcmp(r->tag, tag)) {
+ return true;
+ }
+ }
+ return false;
+}
+
static int64_t blkdebug_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file);
@@ -464,7 +580,10 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_aio_readv = blkdebug_aio_readv,
.bdrv_aio_writev = blkdebug_aio_writev,
- .bdrv_debug_event = blkdebug_debug_event,
+ .bdrv_debug_event = blkdebug_debug_event,
+ .bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
+ .bdrv_debug_resume = blkdebug_debug_resume,
+ .bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
};
static void bdrv_blkdebug_init(void)