#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/config-file.h"
+#include "block/block-io.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "qemu/module.h"
#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;
};
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;
}
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, errp);
- if (!bs->file) {
- ret = -EINVAL;
+ ret = bdrv_open_file_child(qemu_opt_get(opts, "x-image"), options, "image",
+ bs, errp);
+ if (ret < 0) {
goto out;
}
+ bdrv_graph_rdlock_main_loop();
+
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
error_setg(errp, "Cannot meet constraints with align %" PRIu64,
s->align);
- goto out;
+ goto out_rdlock;
}
align = MAX(s->align, bs->file->bs->bl.request_alignment);
!QEMU_IS_ALIGNED(s->max_transfer, align))) {
error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
s->max_transfer);
- goto out;
+ goto out_rdlock;
}
s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
!QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
s->opt_write_zero);
- goto out;
+ goto out_rdlock;
}
s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
MAX(s->opt_write_zero, align)))) {
error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
s->max_write_zero);
- goto out;
+ goto out_rdlock;
}
s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
!QEMU_IS_ALIGNED(s->opt_discard, align))) {
error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
s->opt_discard);
- goto out;
+ goto out_rdlock;
}
s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
MAX(s->opt_discard, align)))) {
error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
s->max_discard);
- goto out;
+ goto out_rdlock;
}
bdrv_debug_event(bs, BLKDBG_NONE);
ret = 0;
+out_rdlock:
+ bdrv_graph_rdunlock_main_loop();
out:
if (ret < 0) {
+ qemu_mutex_destroy(&s->lock);
g_free(s->config_file);
}
qemu_opts_del(opts);
return ret;
}
-static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- BlkdebugIOType iotype)
+static int coroutine_fn rule_check(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, BlkdebugIOType iotype)
{
BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
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();
return -error;
}
-static int coroutine_fn
-blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn GRAPH_RDLOCK
+blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ QEMUIOVector *qiov, BdrvRequestFlags flags)
{
int err;
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
}
-static int coroutine_fn
-blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn GRAPH_RDLOCK
+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 GRAPH_RDLOCK coroutine_fn blkdebug_co_flush(BlockDriverState *bs)
{
int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
return bdrv_co_flush(bs->file->bs);
}
-static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
- int64_t offset, int bytes,
- BdrvRequestFlags flags)
+static int coroutine_fn GRAPH_RDLOCK
+blkdebug_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ BdrvRequestFlags flags)
{
uint32_t align = MAX(bs->bl.request_alignment,
bs->bl.pwrite_zeroes_alignment);
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
}
-static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
- int64_t offset, int bytes)
+static int coroutine_fn GRAPH_RDLOCK
+blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
{
uint32_t align = bs->bl.pdiscard_alignment;
int err;
return bdrv_co_pdiscard(bs->file, offset, bytes);
}
-static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
- bool want_zero,
- int64_t offset,
- int64_t bytes,
- int64_t *pnum,
- int64_t *map,
- BlockDriverState **file)
+static int coroutine_fn GRAPH_RDLOCK
+blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
+ int64_t bytes, int64_t *pnum, int64_t *map,
+ BlockDriverState **file)
{
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;
}
}
+/* Called with lock held. */
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
- int *action_count)
+ int *action_count, int *new_state)
{
BDRVBlkdebugState *s = bs->opaque;
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:
}
}
-static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
+static void coroutine_fn
+blkdebug_co_debug_event(BlockDriverState *bs, BlkdebugEvent event)
{
BDRVBlkdebugState *s = bs->opaque;
struct BlkdebugRule *rule, *next;
+ int new_state;
int actions_count[ACTION__MAX] = { 0 };
assert((int)event >= 0 && event < BLKDBG__MAX);
- s->new_state = s->state;
- QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
- process_rule(bs, rule, actions_count);
+ 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;
}
+/* Called with lock held. May temporarily release lock. */
static int resume_req_by_tag(BDRVBlkdebugState *s, const char *tag, bool all)
{
BlkdebugSuspendedReq *r;
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;
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);
}
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 &&
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;
return false;
}
-static int64_t blkdebug_getlength(BlockDriverState *bs)
+static int64_t coroutine_fn GRAPH_RDLOCK
+blkdebug_co_getlength(BlockDriverState *bs)
{
- return bdrv_getlength(bs->file->bs);
+ return bdrv_co_getlength(bs->file->bs);
}
-static void blkdebug_refresh_filename(BlockDriverState *bs)
+static void GRAPH_RDLOCK blkdebug_refresh_filename(BlockDriverState *bs)
{
BDRVBlkdebugState *s = bs->opaque;
const QDictEntry *e;
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
.bdrv_child_perm = blkdebug_child_perm,
- .bdrv_getlength = blkdebug_getlength,
+ .bdrv_co_getlength = blkdebug_co_getlength,
.bdrv_refresh_filename = blkdebug_refresh_filename,
.bdrv_refresh_limits = blkdebug_refresh_limits,
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
.bdrv_co_block_status = blkdebug_co_block_status,
- .bdrv_debug_event = blkdebug_debug_event,
+ .bdrv_co_debug_event = blkdebug_co_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
.bdrv_debug_remove_breakpoint
= blkdebug_debug_remove_breakpoint,