#include "qapi/qobject-input-visitor.h"
#include "sysemu/qtest.h"
+/* All APIs are thread-safe */
+
typedef struct BDRVBlkdebugState {
- int state;
- int new_state;
+ /* IN: initialized in blkdebug_open() and never changed */
uint64_t align;
uint64_t max_transfer;
uint64_t opt_write_zero;
uint64_t max_write_zero;
uint64_t opt_discard;
uint64_t max_discard;
-
+ char *config_file; /* For blkdebug_refresh_filename() */
+ /* initialized in blkdebug_parse_perms() */
uint64_t take_child_perms;
uint64_t unshare_child_perms;
- /* For blkdebug_refresh_filename() */
- char *config_file;
-
+ /* State. Protected by lock */
+ int state;
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
+ QemuMutex lock;
} BDRVBlkdebugState;
typedef struct BlkdebugAIOCB {
} BlkdebugAIOCB;
typedef struct BlkdebugSuspendedReq {
+ /* IN: initialized in suspend_request() */
Coroutine *co;
char *tag;
+
+ /* List entry protected BDRVBlkdebugState's lock */
QLIST_ENTRY(BlkdebugSuspendedReq) next;
} BlkdebugSuspendedReq;
ACTION_INJECT_ERROR,
ACTION_SET_STATE,
ACTION_SUSPEND,
+ ACTION__MAX,
};
typedef struct BlkdebugRule {
+ /* IN: initialized in add_rule() or blkdebug_debug_breakpoint() */
BlkdebugEvent event;
int action;
int state;
char *tag;
} suspend;
} options;
+
+ /* List entries protected BDRVBlkdebugState's lock */
QLIST_ENTRY(BlkdebugRule) next;
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
} BlkdebugRule;
};
/* Add the rule */
+ qemu_mutex_lock(&s->lock);
QLIST_INSERT_HEAD(&s->rules[event], rule, next);
+ qemu_mutex_unlock(&s->lock);
return 0;
}
+/* Called with lock held or from .bdrv_close */
static void remove_rule(BlkdebugRule *rule)
{
switch (rule->action) {
}
}
- qemu_config_parse_qdict(options, config_groups, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!qemu_config_parse_qdict(options, config_groups, errp)) {
ret = -EINVAL;
goto fail;
}
{
BDRVBlkdebugState *s = bs->opaque;
QemuOpts *opts;
- Error *local_err = NULL;
int ret;
uint64_t align;
+ qemu_mutex_init(&s->lock);
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
ret = -EINVAL;
}
/* Open the image file */
- bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
- bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, &local_err);
- if (local_err) {
- ret = -EINVAL;
- error_propagate(errp, local_err);
+ ret = bdrv_open_file_child(qemu_opt_get(opts, "x-image"), options, "image",
+ bs, errp);
+ if (ret < 0) {
goto out;
}
ret = 0;
out:
if (ret < 0) {
+ qemu_mutex_destroy(&s->lock);
g_free(s->config_file);
}
qemu_opts_del(opts);
int error;
bool immediately;
+ qemu_mutex_lock(&s->lock);
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
uint64_t inject_offset = rule->options.inject.offset;
}
if (!rule || !rule->options.inject.error) {
+ qemu_mutex_unlock(&s->lock);
return 0;
}
remove_rule(rule);
}
+ qemu_mutex_unlock(&s->lock);
if (!immediately) {
aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
qemu_coroutine_yield();
}
static int coroutine_fn
-blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ QEMUIOVector *qiov, BdrvRequestFlags flags)
{
int err;
}
static int coroutine_fn
-blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+blkdebug_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ QEMUIOVector *qiov, BdrvRequestFlags flags)
{
int err;
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
}
-static int blkdebug_co_flush(BlockDriverState *bs)
+static int coroutine_fn blkdebug_co_flush(BlockDriverState *bs)
{
int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
}
static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
- int64_t offset, int bytes,
+ int64_t offset, int64_t bytes,
BdrvRequestFlags flags)
{
uint32_t align = MAX(bs->bl.request_alignment,
}
static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
- int64_t offset, int bytes)
+ int64_t offset, int64_t bytes)
{
uint32_t align = bs->bl.pdiscard_alignment;
int err;
}
g_free(s->config_file);
+ qemu_mutex_destroy(&s->lock);
}
+/* Called with lock held. */
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
{
BDRVBlkdebugState *s = bs->opaque;
- BlkdebugSuspendedReq r;
+ BlkdebugSuspendedReq *r;
- r = (BlkdebugSuspendedReq) {
- .co = qemu_coroutine_self(),
- .tag = g_strdup(rule->options.suspend.tag),
- };
+ r = g_new(BlkdebugSuspendedReq, 1);
+
+ r->co = qemu_coroutine_self();
+ r->tag = g_strdup(rule->options.suspend.tag);
remove_rule(rule);
- QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
+ QLIST_INSERT_HEAD(&s->suspended_reqs, r, next);
if (!qtest_enabled()) {
- printf("blkdebug: Suspended request '%s'\n", r.tag);
- }
- qemu_coroutine_yield();
- if (!qtest_enabled()) {
- printf("blkdebug: Resuming request '%s'\n", r.tag);
+ printf("blkdebug: Suspended request '%s'\n", r->tag);
}
-
- QLIST_REMOVE(&r, next);
- g_free(r.tag);
}
-static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
- bool injected)
+/* Called with lock held. */
+static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
+ int *action_count, int *new_state)
{
BDRVBlkdebugState *s = bs->opaque;
/* Only process rules for the current state */
if (rule->state && rule->state != s->state) {
- return injected;
+ return;
}
/* Take the action */
+ action_count[rule->action]++;
switch (rule->action) {
case ACTION_INJECT_ERROR:
- if (!injected) {
+ if (action_count[ACTION_INJECT_ERROR] == 1) {
QSIMPLEQ_INIT(&s->active_rules);
- injected = true;
}
QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
break;
case ACTION_SET_STATE:
- s->new_state = rule->options.set_state.new_state;
+ *new_state = rule->options.set_state.new_state;
break;
case ACTION_SUSPEND:
suspend_request(bs, rule);
break;
}
- return injected;
}
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
{
BDRVBlkdebugState *s = bs->opaque;
struct BlkdebugRule *rule, *next;
- bool injected;
+ int new_state;
+ int actions_count[ACTION__MAX] = { 0 };
assert((int)event >= 0 && event < BLKDBG__MAX);
- injected = false;
- s->new_state = s->state;
- QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
- injected = process_rule(bs, rule, injected);
+ WITH_QEMU_LOCK_GUARD(&s->lock) {
+ new_state = s->state;
+ QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
+ process_rule(bs, rule, actions_count, &new_state);
+ }
+ s->state = new_state;
+ }
+
+ while (actions_count[ACTION_SUSPEND] > 0) {
+ qemu_coroutine_yield();
+ actions_count[ACTION_SUSPEND]--;
}
- s->state = s->new_state;
}
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
.options.suspend.tag = g_strdup(tag),
};
+ qemu_mutex_lock(&s->lock);
QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
+ qemu_mutex_unlock(&s->lock);
return 0;
}
-static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
+/* Called with lock held. May temporarily release lock. */
+static int resume_req_by_tag(BDRVBlkdebugState *s, const char *tag, bool all)
{
- BDRVBlkdebugState *s = bs->opaque;
- BlkdebugSuspendedReq *r, *next;
+ BlkdebugSuspendedReq *r;
- QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
+retry:
+ /*
+ * No need for _SAFE, since a different coroutine can remove another node
+ * (not the current one) in this list, and when the current one is removed
+ * the iteration starts back from beginning anyways.
+ */
+ QLIST_FOREACH(r, &s->suspended_reqs, next) {
if (!strcmp(r->tag, tag)) {
- qemu_coroutine_enter(r->co);
+ Coroutine *co = r->co;
+
+ if (!qtest_enabled()) {
+ printf("blkdebug: Resuming request '%s'\n", r->tag);
+ }
+
+ QLIST_REMOVE(r, next);
+ g_free(r->tag);
+ g_free(r);
+
+ qemu_mutex_unlock(&s->lock);
+ qemu_coroutine_enter(co);
+ qemu_mutex_lock(&s->lock);
+
+ if (all) {
+ goto retry;
+ }
return 0;
}
}
return -ENOENT;
}
+static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ QEMU_LOCK_GUARD(&s->lock);
+ return resume_req_by_tag(s, tag, false);
+}
+
static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
const char *tag)
{
BDRVBlkdebugState *s = bs->opaque;
- BlkdebugSuspendedReq *r, *r_next;
BlkdebugRule *rule, *next;
int i, ret = -ENOENT;
+ QEMU_LOCK_GUARD(&s->lock);
for (i = 0; i < BLKDBG__MAX; i++) {
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
if (rule->action == ACTION_SUSPEND &&
}
}
}
- QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
- if (!strcmp(r->tag, tag)) {
- qemu_coroutine_enter(r->co);
- ret = 0;
- }
+ if (resume_req_by_tag(s, tag, true) == 0) {
+ ret = 0;
}
return ret;
}
BDRVBlkdebugState *s = bs->opaque;
BlkdebugSuspendedReq *r;
+ QEMU_LOCK_GUARD(&s->lock);
QLIST_FOREACH(r, &s->suspended_reqs, next) {
if (!strcmp(r->tag, tag)) {
return true;