Message ID | 20170605123908.18777-20-pbonzini@redhat.com |
---|---|
State | New |
Headers | show |
On Mon 05 Jun 2017 02:39:08 PM CEST, Paolo Bonzini wrote: > I'm not trying too hard yet. Later, with multiqueue support, > this may cause mutex contention or cacheline bouncing. > > Cc: Alberto Garcia <berto@igalia.com> > Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- Reviewed-by: Alberto Garcia <berto@igalia.com> Berto
On Mon 05 Jun 2017 02:39:08 PM CEST, Paolo Bonzini wrote: > @@ -102,6 +107,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, > > assert(cookie->type < BLOCK_MAX_IOTYPE); > > + qemu_mutex_lock(&stats->lock); QEMU crashes because this lock is not initialized if you do aio_read directly from a BDS not attached to a BlockBackend (should that operation be legal at all?) $ qemu-system-x86_64 -monitor stdio \ -blockdev driver=qcow2,file.driver=file,file.filename=hd0.img,node-name=hd0 (qemu) qemu-io hd0 "aio_read 0 1M" qemu-system-x86_64: util/qemu-thread-posix.c:64: qemu_mutex_lock_impl: Assertion `mutex->initialized' failed. Aborted Berto
On Thu 24 May 2018 03:35:50 PM CEST, Alberto Garcia wrote: > On Mon 05 Jun 2017 02:39:08 PM CEST, Paolo Bonzini wrote: >> @@ -102,6 +107,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, >> >> assert(cookie->type < BLOCK_MAX_IOTYPE); >> >> + qemu_mutex_lock(&stats->lock); > > QEMU crashes because this lock is not initialized if you do aio_read > directly from a BDS not attached to a BlockBackend (should that > operation be legal at all?) I see, hmp_qemu_io() creates a temporary BlockBackend in these situations, so I guess what we need is something like this: diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 9b3cd00af6..7b195ec4e3 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1294,6 +1294,7 @@ static void aio_read_done(void *opaque, int ret) print_report("read", &t2, ctx->offset, ctx->qiov.size, ctx->qiov.size, 1, ctx->Cflag); out: + blk_unref(ctx->blk); qemu_io_free(ctx->buf); qemu_iovec_destroy(&ctx->qiov); g_free(ctx); @@ -1392,6 +1393,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) gettimeofday(&ctx->t1, NULL); block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, BLOCK_ACCT_READ); + blk_ref(blk); blk_aio_preadv(blk, ctx->offset, &ctx->qiov, 0, aio_read_done, ctx); return 0; } Berto
On 24/05/2018 15:35, Alberto Garcia wrote: > On Mon 05 Jun 2017 02:39:08 PM CEST, Paolo Bonzini wrote: >> @@ -102,6 +107,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, >> >> assert(cookie->type < BLOCK_MAX_IOTYPE); >> >> + qemu_mutex_lock(&stats->lock); > > QEMU crashes because this lock is not initialized if you do aio_read > directly from a BDS not attached to a BlockBackend (should that > operation be legal at all?) > > $ qemu-system-x86_64 -monitor stdio \ > -blockdev driver=qcow2,file.driver=file,file.filename=hd0.img,node-name=hd0 > > (qemu) qemu-io hd0 "aio_read 0 1M" > qemu-system-x86_64: util/qemu-thread-posix.c:64: qemu_mutex_lock_impl: Assertion `mutex->initialized' failed. > Aborted The lock is initialized but it has been destroyed via qemu_mutex_destroy before you reach this point. So the bug is actually much more than just the uninitialized lock! The destruction happens when blk_delete calls block_acct_cleanup. It should be fixed by Greg Kurz's patch that adds a bdrv_drained_begin/end pair to bdrv_root_unref_child. Thanks, Paolo
diff --git a/block/accounting.c b/block/accounting.c index ce6dbf7760..87ef5bbfaa 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -34,6 +34,7 @@ static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; void block_acct_init(BlockAcctStats *stats) { + qemu_mutex_init(&stats->lock); if (qtest_enabled()) { clock_type = QEMU_CLOCK_VIRTUAL; } @@ -52,6 +53,7 @@ void block_acct_cleanup(BlockAcctStats *stats) QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) { g_free(s); } + qemu_mutex_destroy(&stats->lock); } void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) @@ -61,12 +63,15 @@ void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) s = g_new0(BlockAcctTimedStats, 1); s->interval_length = interval_length; + s->stats = stats; + qemu_mutex_lock(&stats->lock); QSLIST_INSERT_HEAD(&stats->intervals, s, entries); for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { timed_average_init(&s->latency[i], clock_type, (uint64_t) interval_length * NANOSECONDS_PER_SECOND); } + qemu_mutex_unlock(&stats->lock); } BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, @@ -102,6 +107,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, assert(cookie->type < BLOCK_MAX_IOTYPE); + qemu_mutex_lock(&stats->lock); + if (failed) { stats->failed_ops[cookie->type]++; } else { @@ -117,6 +124,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, timed_average_account(&s->latency[cookie->type], latency_ns); } } + + qemu_mutex_unlock(&stats->lock); } void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) @@ -137,18 +146,23 @@ void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type) * not. The reason is that invalid requests are accounted during their * submission, therefore there's no actual I/O involved. */ + qemu_mutex_lock(&stats->lock); stats->invalid_ops[type]++; if (stats->account_invalid) { stats->last_access_time_ns = qemu_clock_get_ns(clock_type); } + qemu_mutex_unlock(&stats->lock); } void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, int num_requests) { assert(type < BLOCK_MAX_IOTYPE); + + qemu_mutex_lock(&stats->lock); stats->merged[type] += num_requests; + qemu_mutex_unlock(&stats->lock); } int64_t block_acct_idle_time_ns(BlockAcctStats *stats) @@ -163,7 +177,9 @@ double block_acct_queue_depth(BlockAcctTimedStats *stats, assert(type < BLOCK_MAX_IOTYPE); + qemu_mutex_lock(&stats->stats->lock); sum = timed_average_sum(&stats->latency[type], &elapsed); + qemu_mutex_unlock(&stats->stats->lock); return (double) sum / elapsed; } diff --git a/include/block/accounting.h b/include/block/accounting.h index 55cb06fdb6..b833d26d6c 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -26,8 +26,10 @@ #define BLOCK_ACCOUNTING_H #include "qemu/timed-average.h" +#include "qemu/thread.h" typedef struct BlockAcctTimedStats BlockAcctTimedStats; +typedef struct BlockAcctStats BlockAcctStats; enum BlockAcctType { BLOCK_ACCT_READ, @@ -37,12 +39,14 @@ enum BlockAcctType { }; struct BlockAcctTimedStats { + BlockAcctStats *stats; TimedAverage latency[BLOCK_MAX_IOTYPE]; unsigned interval_length; /* in seconds */ QSLIST_ENTRY(BlockAcctTimedStats) entries; }; -typedef struct BlockAcctStats { +struct BlockAcctStats { + QemuMutex lock; uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; uint64_t nr_ops[BLOCK_MAX_IOTYPE]; uint64_t invalid_ops[BLOCK_MAX_IOTYPE]; @@ -53,7 +57,7 @@ typedef struct BlockAcctStats { QSLIST_HEAD(, BlockAcctTimedStats) intervals; bool account_invalid; bool account_failed; -} BlockAcctStats; +}; typedef struct BlockAcctCookie { int64_t bytes;