mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging
Block layer patches - Code cleanups around block graph modification - Simplify drain - coroutine_fn correctness fixes, including splitting generated coroutine wrappers into co_wrapper (to be called only from non-coroutine context) and co_wrapper_mixed (both coroutine and non-coroutine context) - Introduce a block graph rwlock # gpg: Signature made Thu 15 Dec 2022 15:08:34 GMT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * tag 'for-upstream' of https://repo.or.cz/qemu/kevin: (50 commits) block: GRAPH_RDLOCK for functions only called by co_wrappers block: use co_wrapper_mixed_bdrv_rdlock in functions taking the rdlock block-coroutine-wrapper.py: introduce annotations that take the graph rdlock Mark assert_bdrv_graph_readable/writable() GRAPH_RD/WRLOCK graph-lock: TSA annotations for lock/unlock functions block: assert that graph read and writes are performed correctly block: remove unnecessary assert_bdrv_graph_writable() block: wrlock in bdrv_replace_child_noperm block: Fix locking in external_snapshot_prepare() test-bdrv-drain: Fix incorrrect drain assumptions clang-tsa: Add macros for shared locks clang-tsa: Add TSA_ASSERT() macro Import clang-tsa.h async: Register/unregister aiocontext in graph lock list graph-lock: Implement guard macros graph-lock: Introduce a lock to protect block graph operations block: Factor out bdrv_drain_all_begin_nopoll() block/dirty-bitmap: convert coroutine-only functions to co_wrapper block: convert bdrv_create to co_wrapper block-coroutine-wrapper.py: support also basic return types ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+23
-2
@@ -129,7 +129,7 @@ static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format,
|
||||
}
|
||||
static void blk_root_drained_begin(BdrvChild *child);
|
||||
static bool blk_root_drained_poll(BdrvChild *child);
|
||||
static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
|
||||
static void blk_root_drained_end(BdrvChild *child);
|
||||
|
||||
static void blk_root_change_media(BdrvChild *child, bool load);
|
||||
static void blk_root_resize(BdrvChild *child);
|
||||
@@ -1424,6 +1424,27 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
|
||||
return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_block_status_above(BlockBackend *blk,
|
||||
BlockDriverState *base,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
IO_CODE();
|
||||
return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum,
|
||||
map, file);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk,
|
||||
BlockDriverState *base,
|
||||
bool include_base, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
IO_CODE();
|
||||
return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset,
|
||||
bytes, pnum);
|
||||
}
|
||||
|
||||
typedef struct BlkRwCo {
|
||||
BlockBackend *blk;
|
||||
int64_t offset;
|
||||
@@ -2556,7 +2577,7 @@ static bool blk_root_drained_poll(BdrvChild *child)
|
||||
return busy || !!blk->in_flight;
|
||||
}
|
||||
|
||||
static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
|
||||
static void blk_root_drained_end(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
assert(blk->quiesce_counter);
|
||||
|
||||
+12
-9
@@ -577,8 +577,9 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int block_copy_block_status(BlockCopyState *s, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
static coroutine_fn int block_copy_block_status(BlockCopyState *s,
|
||||
int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
int64_t num;
|
||||
BlockDriverState *base;
|
||||
@@ -590,8 +591,8 @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset,
|
||||
base = NULL;
|
||||
}
|
||||
|
||||
ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num,
|
||||
NULL, NULL);
|
||||
ret = bdrv_co_block_status_above(s->source->bs, base, offset, bytes, &num,
|
||||
NULL, NULL);
|
||||
if (ret < 0 || num < s->cluster_size) {
|
||||
/*
|
||||
* On error or if failed to obtain large enough chunk just fallback to
|
||||
@@ -613,8 +614,9 @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset,
|
||||
* Check if the cluster starting at offset is allocated or not.
|
||||
* return via pnum the number of contiguous clusters sharing this allocation.
|
||||
*/
|
||||
static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
|
||||
int64_t *pnum)
|
||||
static int coroutine_fn block_copy_is_cluster_allocated(BlockCopyState *s,
|
||||
int64_t offset,
|
||||
int64_t *pnum)
|
||||
{
|
||||
BlockDriverState *bs = s->source->bs;
|
||||
int64_t count, total_count = 0;
|
||||
@@ -624,7 +626,7 @@ static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
|
||||
while (true) {
|
||||
ret = bdrv_is_allocated(bs, offset, bytes, &count);
|
||||
ret = bdrv_co_is_allocated(bs, offset, bytes, &count);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -669,8 +671,9 @@ void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes)
|
||||
* @return 0 when the cluster at @offset was unallocated,
|
||||
* 1 otherwise, and -ret on error.
|
||||
*/
|
||||
int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
||||
int64_t offset, int64_t *count)
|
||||
int64_t coroutine_fn block_copy_reset_unallocated(BlockCopyState *s,
|
||||
int64_t offset,
|
||||
int64_t *count)
|
||||
{
|
||||
int ret;
|
||||
int64_t clusters, bytes;
|
||||
|
||||
+4
-7
@@ -30,20 +30,17 @@
|
||||
|
||||
/* Base structure for argument packing structures */
|
||||
typedef struct BdrvPollCo {
|
||||
BlockDriverState *bs;
|
||||
AioContext *ctx;
|
||||
bool in_progress;
|
||||
int ret;
|
||||
Coroutine *co; /* Keep pointer here for debugging */
|
||||
} BdrvPollCo;
|
||||
|
||||
static inline int bdrv_poll_co(BdrvPollCo *s)
|
||||
static inline void bdrv_poll_co(BdrvPollCo *s)
|
||||
{
|
||||
assert(!qemu_in_coroutine());
|
||||
|
||||
bdrv_coroutine_enter(s->bs, s->co);
|
||||
BDRV_POLL_WHILE(s->bs, s->in_progress);
|
||||
|
||||
return s->ret;
|
||||
aio_co_enter(s->ctx, s->co);
|
||||
AIO_WAIT_WHILE(s->ctx, s->in_progress);
|
||||
}
|
||||
|
||||
#endif /* BLOCK_BLOCK_GEN_H */
|
||||
|
||||
+2
-2
@@ -155,8 +155,8 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
break;
|
||||
}
|
||||
/* Copy if allocated above the base */
|
||||
ret = bdrv_is_allocated_above(blk_bs(s->top), s->base_overlay, true,
|
||||
offset, COMMIT_BUFFER_SIZE, &n);
|
||||
ret = blk_co_is_allocated_above(s->top, s->base_overlay, true,
|
||||
offset, COMMIT_BUFFER_SIZE, &n);
|
||||
copy = (ret > 0);
|
||||
trace_commit_one_iteration(s, offset, n, ret);
|
||||
if (copy) {
|
||||
|
||||
+12
-9
@@ -37,9 +37,11 @@
|
||||
* the I/O API.
|
||||
*/
|
||||
|
||||
int coroutine_fn bdrv_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *res, BdrvCheckMode fix);
|
||||
int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp);
|
||||
int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
|
||||
|
||||
int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp);
|
||||
|
||||
int coroutine_fn
|
||||
bdrv_co_common_block_status_above(BlockDriverState *bs,
|
||||
@@ -53,10 +55,11 @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState **file,
|
||||
int *depth);
|
||||
|
||||
int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs,
|
||||
QEMUIOVector *qiov, int64_t pos);
|
||||
int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs,
|
||||
QEMUIOVector *qiov, int64_t pos);
|
||||
int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
|
||||
|
||||
int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
|
||||
|
||||
int coroutine_fn
|
||||
nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
|
||||
@@ -71,7 +74,7 @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
|
||||
* the "I/O or GS" API.
|
||||
*/
|
||||
|
||||
int generated_co_wrapper
|
||||
int co_wrapper_mixed_bdrv_rdlock
|
||||
bdrv_common_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
bool include_base,
|
||||
@@ -82,7 +85,7 @@ bdrv_common_block_status_above(BlockDriverState *bs,
|
||||
int64_t *map,
|
||||
BlockDriverState **file,
|
||||
int *depth);
|
||||
int generated_co_wrapper
|
||||
int co_wrapper_mixed
|
||||
nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp);
|
||||
|
||||
#endif /* BLOCK_COROUTINES_H */
|
||||
|
||||
+1
-1
@@ -703,7 +703,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv,
|
||||
}
|
||||
|
||||
/* Create protocol layer */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
ret = bdrv_co_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+2
-86
@@ -388,7 +388,7 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
* not fail.
|
||||
* This function doesn't release corresponding BdrvDirtyBitmap.
|
||||
*/
|
||||
static int coroutine_fn
|
||||
int coroutine_fn
|
||||
bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -399,45 +399,6 @@ bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct BdrvRemovePersistentDirtyBitmapCo {
|
||||
BlockDriverState *bs;
|
||||
const char *name;
|
||||
Error **errp;
|
||||
int ret;
|
||||
} BdrvRemovePersistentDirtyBitmapCo;
|
||||
|
||||
static void coroutine_fn
|
||||
bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque)
|
||||
{
|
||||
BdrvRemovePersistentDirtyBitmapCo *s = opaque;
|
||||
|
||||
s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
if (qemu_in_coroutine()) {
|
||||
return bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp);
|
||||
} else {
|
||||
Coroutine *co;
|
||||
BdrvRemovePersistentDirtyBitmapCo s = {
|
||||
.bs = bs,
|
||||
.name = name,
|
||||
.errp = errp,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_remove_persistent_dirty_bitmap_entry,
|
||||
&s);
|
||||
bdrv_coroutine_enter(bs, co);
|
||||
BDRV_POLL_WHILE(bs, s.ret == -EINPROGRESS);
|
||||
|
||||
return s.ret;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs)
|
||||
{
|
||||
@@ -447,7 +408,7 @@ bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool coroutine_fn
|
||||
bool coroutine_fn
|
||||
bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
uint32_t granularity, Error **errp)
|
||||
{
|
||||
@@ -470,51 +431,6 @@ bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
return drv->bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
||||
}
|
||||
|
||||
typedef struct BdrvCanStoreNewDirtyBitmapCo {
|
||||
BlockDriverState *bs;
|
||||
const char *name;
|
||||
uint32_t granularity;
|
||||
Error **errp;
|
||||
bool ret;
|
||||
|
||||
bool in_progress;
|
||||
} BdrvCanStoreNewDirtyBitmapCo;
|
||||
|
||||
static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque)
|
||||
{
|
||||
BdrvCanStoreNewDirtyBitmapCo *s = opaque;
|
||||
|
||||
s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity,
|
||||
s->errp);
|
||||
s->in_progress = false;
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
uint32_t granularity, Error **errp)
|
||||
{
|
||||
IO_CODE();
|
||||
if (qemu_in_coroutine()) {
|
||||
return bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
||||
} else {
|
||||
Coroutine *co;
|
||||
BdrvCanStoreNewDirtyBitmapCo s = {
|
||||
.bs = bs,
|
||||
.name = name,
|
||||
.granularity = granularity,
|
||||
.errp = errp,
|
||||
.in_progress = true,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_can_store_new_dirty_bitmap_entry,
|
||||
&s);
|
||||
bdrv_coroutine_enter(bs, co);
|
||||
BDRV_POLL_WHILE(bs, s.in_progress);
|
||||
|
||||
return s.ret;
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Graph lock: rwlock to protect block layer graph manipulations (add/remove
|
||||
* edges and nodes)
|
||||
*
|
||||
* Copyright (c) 2022 Red Hat
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "block/graph-lock.h"
|
||||
#include "block/block.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
/* Dummy lock object to use for Thread Safety Analysis (TSA) */
|
||||
BdrvGraphLock graph_lock;
|
||||
|
||||
/* Protects the list of aiocontext and orphaned_reader_count */
|
||||
static QemuMutex aio_context_list_lock;
|
||||
|
||||
/* Written and read with atomic operations. */
|
||||
static int has_writer;
|
||||
|
||||
/*
|
||||
* A reader coroutine could move from an AioContext to another.
|
||||
* If this happens, there is no problem from the point of view of
|
||||
* counters. The problem is that the total count becomes
|
||||
* unbalanced if one of the two AioContexts gets deleted.
|
||||
* The count of readers must remain correct, so the AioContext's
|
||||
* balance is transferred to this glboal variable.
|
||||
* Protected by aio_context_list_lock.
|
||||
*/
|
||||
static uint32_t orphaned_reader_count;
|
||||
|
||||
/* Queue of readers waiting for the writer to finish */
|
||||
static CoQueue reader_queue;
|
||||
|
||||
struct BdrvGraphRWlock {
|
||||
/* How many readers are currently reading the graph. */
|
||||
uint32_t reader_count;
|
||||
|
||||
/*
|
||||
* List of BdrvGraphRWlock kept in graph-lock.c
|
||||
* Protected by aio_context_list_lock
|
||||
*/
|
||||
QTAILQ_ENTRY(BdrvGraphRWlock) next_aio;
|
||||
};
|
||||
|
||||
/*
|
||||
* List of BdrvGraphRWlock. This list ensures that each BdrvGraphRWlock
|
||||
* can safely modify only its own counter, avoid reading/writing
|
||||
* others and thus improving performances by avoiding cacheline bounces.
|
||||
*/
|
||||
static QTAILQ_HEAD(, BdrvGraphRWlock) aio_context_list =
|
||||
QTAILQ_HEAD_INITIALIZER(aio_context_list);
|
||||
|
||||
static void __attribute__((__constructor__)) bdrv_init_graph_lock(void)
|
||||
{
|
||||
qemu_mutex_init(&aio_context_list_lock);
|
||||
qemu_co_queue_init(&reader_queue);
|
||||
}
|
||||
|
||||
void register_aiocontext(AioContext *ctx)
|
||||
{
|
||||
ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1);
|
||||
QEMU_LOCK_GUARD(&aio_context_list_lock);
|
||||
assert(ctx->bdrv_graph->reader_count == 0);
|
||||
QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio);
|
||||
}
|
||||
|
||||
void unregister_aiocontext(AioContext *ctx)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&aio_context_list_lock);
|
||||
orphaned_reader_count += ctx->bdrv_graph->reader_count;
|
||||
QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio);
|
||||
g_free(ctx->bdrv_graph);
|
||||
}
|
||||
|
||||
static uint32_t reader_count(void)
|
||||
{
|
||||
BdrvGraphRWlock *brdv_graph;
|
||||
uint32_t rd;
|
||||
|
||||
QEMU_LOCK_GUARD(&aio_context_list_lock);
|
||||
|
||||
/* rd can temporarly be negative, but the total will *always* be >= 0 */
|
||||
rd = orphaned_reader_count;
|
||||
QTAILQ_FOREACH(brdv_graph, &aio_context_list, next_aio) {
|
||||
rd += qatomic_read(&brdv_graph->reader_count);
|
||||
}
|
||||
|
||||
/* shouldn't overflow unless there are 2^31 readers */
|
||||
assert((int32_t)rd >= 0);
|
||||
return rd;
|
||||
}
|
||||
|
||||
void bdrv_graph_wrlock(void)
|
||||
{
|
||||
GLOBAL_STATE_CODE();
|
||||
assert(!qatomic_read(&has_writer));
|
||||
|
||||
/* Make sure that constantly arriving new I/O doesn't cause starvation */
|
||||
bdrv_drain_all_begin_nopoll();
|
||||
|
||||
/*
|
||||
* reader_count == 0: this means writer will read has_reader as 1
|
||||
* reader_count >= 1: we don't know if writer read has_writer == 0 or 1,
|
||||
* but we need to wait.
|
||||
* Wait by allowing other coroutine (and possible readers) to continue.
|
||||
*/
|
||||
do {
|
||||
/*
|
||||
* has_writer must be 0 while polling, otherwise we get a deadlock if
|
||||
* any callback involved during AIO_WAIT_WHILE() tries to acquire the
|
||||
* reader lock.
|
||||
*/
|
||||
qatomic_set(&has_writer, 0);
|
||||
AIO_WAIT_WHILE(qemu_get_aio_context(), reader_count() >= 1);
|
||||
qatomic_set(&has_writer, 1);
|
||||
|
||||
/*
|
||||
* We want to only check reader_count() after has_writer = 1 is visible
|
||||
* to other threads. That way no more readers can sneak in after we've
|
||||
* determined reader_count() == 0.
|
||||
*/
|
||||
smp_mb();
|
||||
} while (reader_count() >= 1);
|
||||
|
||||
bdrv_drain_all_end();
|
||||
}
|
||||
|
||||
void bdrv_graph_wrunlock(void)
|
||||
{
|
||||
GLOBAL_STATE_CODE();
|
||||
QEMU_LOCK_GUARD(&aio_context_list_lock);
|
||||
assert(qatomic_read(&has_writer));
|
||||
|
||||
/*
|
||||
* No need for memory barriers, this works in pair with
|
||||
* the slow path of rdlock() and both take the lock.
|
||||
*/
|
||||
qatomic_store_release(&has_writer, 0);
|
||||
|
||||
/* Wake up all coroutine that are waiting to read the graph */
|
||||
qemu_co_enter_all(&reader_queue, &aio_context_list_lock);
|
||||
}
|
||||
|
||||
void coroutine_fn bdrv_graph_co_rdlock(void)
|
||||
{
|
||||
BdrvGraphRWlock *bdrv_graph;
|
||||
bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
|
||||
|
||||
/* Do not lock if in main thread */
|
||||
if (qemu_in_main_thread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
qatomic_set(&bdrv_graph->reader_count,
|
||||
bdrv_graph->reader_count + 1);
|
||||
/* make sure writer sees reader_count before we check has_writer */
|
||||
smp_mb();
|
||||
|
||||
/*
|
||||
* has_writer == 0: this means writer will read reader_count as >= 1
|
||||
* has_writer == 1: we don't know if writer read reader_count == 0
|
||||
* or > 0, but we need to wait anyways because
|
||||
* it will write.
|
||||
*/
|
||||
if (!qatomic_read(&has_writer)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronize access with reader_count() in bdrv_graph_wrlock().
|
||||
* Case 1:
|
||||
* If this critical section gets executed first, reader_count will
|
||||
* decrease and the reader will go to sleep.
|
||||
* Then the writer will read reader_count that does not take into
|
||||
* account this reader, and if there's no other reader it will
|
||||
* enter the write section.
|
||||
* Case 2:
|
||||
* If reader_count() critical section gets executed first,
|
||||
* then writer will read reader_count >= 1.
|
||||
* It will wait in AIO_WAIT_WHILE(), but once it releases the lock
|
||||
* we will enter this critical section and call aio_wait_kick().
|
||||
*/
|
||||
WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) {
|
||||
/*
|
||||
* Additional check when we use the above lock to synchronize
|
||||
* with bdrv_graph_wrunlock().
|
||||
* Case 1:
|
||||
* If this gets executed first, has_writer is still 1, so we reduce
|
||||
* reader_count and go to sleep.
|
||||
* Then the writer will set has_writer to 0 and wake up all readers,
|
||||
* us included.
|
||||
* Case 2:
|
||||
* If bdrv_graph_wrunlock() critical section gets executed first,
|
||||
* then it will set has_writer to 0 and wake up all other readers.
|
||||
* Then we execute this critical section, and therefore must check
|
||||
* again for has_writer, otherwise we sleep without any writer
|
||||
* actually running.
|
||||
*/
|
||||
if (!qatomic_read(&has_writer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* slow path where reader sleeps */
|
||||
bdrv_graph->reader_count--;
|
||||
aio_wait_kick();
|
||||
qemu_co_queue_wait(&reader_queue, &aio_context_list_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void coroutine_fn bdrv_graph_co_rdunlock(void)
|
||||
{
|
||||
BdrvGraphRWlock *bdrv_graph;
|
||||
bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
|
||||
|
||||
/* Do not lock if in main thread */
|
||||
if (qemu_in_main_thread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
qatomic_store_release(&bdrv_graph->reader_count,
|
||||
bdrv_graph->reader_count - 1);
|
||||
/* make sure writer sees reader_count before we check has_writer */
|
||||
smp_mb();
|
||||
|
||||
/*
|
||||
* has_writer == 0: this means reader will read reader_count decreased
|
||||
* has_writer == 1: we don't know if writer read reader_count old or
|
||||
* new. Therefore, kick again so on next iteration
|
||||
* writer will for sure read the updated value.
|
||||
*/
|
||||
if (qatomic_read(&has_writer)) {
|
||||
aio_wait_kick();
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_graph_rdlock_main_loop(void)
|
||||
{
|
||||
GLOBAL_STATE_CODE();
|
||||
assert(!qemu_in_coroutine());
|
||||
}
|
||||
|
||||
void bdrv_graph_rdunlock_main_loop(void)
|
||||
{
|
||||
GLOBAL_STATE_CODE();
|
||||
assert(!qemu_in_coroutine());
|
||||
}
|
||||
|
||||
void assert_bdrv_graph_readable(void)
|
||||
{
|
||||
assert(qemu_in_main_thread() || reader_count());
|
||||
}
|
||||
|
||||
void assert_bdrv_graph_writable(void)
|
||||
{
|
||||
assert(qemu_in_main_thread());
|
||||
assert(qatomic_read(&has_writer));
|
||||
}
|
||||
+128
-245
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@ block_ss.add(files(
|
||||
'blkverify.c',
|
||||
'block-backend.c',
|
||||
'block-copy.c',
|
||||
'graph-lock.c',
|
||||
'commit.c',
|
||||
'copy-on-read.c',
|
||||
'preallocate.c',
|
||||
@@ -137,6 +138,7 @@ block_gen_c = custom_target('block-gen.c',
|
||||
output: 'block-gen.c',
|
||||
input: files(
|
||||
'../include/block/block-io.h',
|
||||
'../include/block/dirty-bitmap.h',
|
||||
'../include/block/block-global-state.h',
|
||||
'../include/sysemu/block-backend-io.h',
|
||||
'coroutines.h'
|
||||
|
||||
+1
-1
@@ -646,7 +646,7 @@ static int coroutine_fn parallels_co_create_opts(BlockDriver *drv,
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
ret = bdrv_co_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
+1
-1
@@ -973,7 +973,7 @@ static int coroutine_fn qcow_co_create_opts(BlockDriver *drv,
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
ret = bdrv_co_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+2
-2
@@ -3871,7 +3871,7 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
ret = bdrv_co_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto finish;
|
||||
}
|
||||
@@ -3886,7 +3886,7 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
|
||||
/* Create and open an external data file (protocol layer) */
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
|
||||
if (val) {
|
||||
ret = bdrv_create_file(val, opts, errp);
|
||||
ret = bdrv_co_create_file(val, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
+21
-7
@@ -262,7 +262,7 @@ static bool coroutine_fn qed_plug_allocating_write_reqs(BDRVQEDState *s)
|
||||
assert(!s->allocating_write_reqs_plugged);
|
||||
if (s->allocating_acb != NULL) {
|
||||
/* Another allocating write came concurrently. This cannot happen
|
||||
* from bdrv_qed_co_drain_begin, but it can happen when the timer runs.
|
||||
* from bdrv_qed_drain_begin, but it can happen when the timer runs.
|
||||
*/
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
return false;
|
||||
@@ -282,9 +282,8 @@ static void coroutine_fn qed_unplug_allocating_write_reqs(BDRVQEDState *s)
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
}
|
||||
|
||||
static void coroutine_fn qed_need_check_timer_entry(void *opaque)
|
||||
static void coroutine_fn qed_need_check_timer(BDRVQEDState *s)
|
||||
{
|
||||
BDRVQEDState *s = opaque;
|
||||
int ret;
|
||||
|
||||
trace_qed_need_check_timer_cb(s);
|
||||
@@ -310,9 +309,20 @@ static void coroutine_fn qed_need_check_timer_entry(void *opaque)
|
||||
(void) ret;
|
||||
}
|
||||
|
||||
static void coroutine_fn qed_need_check_timer_entry(void *opaque)
|
||||
{
|
||||
BDRVQEDState *s = opaque;
|
||||
|
||||
qed_need_check_timer(opaque);
|
||||
bdrv_dec_in_flight(s->bs);
|
||||
}
|
||||
|
||||
static void qed_need_check_timer_cb(void *opaque)
|
||||
{
|
||||
BDRVQEDState *s = opaque;
|
||||
Coroutine *co = qemu_coroutine_create(qed_need_check_timer_entry, opaque);
|
||||
|
||||
bdrv_inc_in_flight(s->bs);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
@@ -355,7 +365,7 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs)
|
||||
static void bdrv_qed_drain_begin(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
@@ -363,8 +373,12 @@ static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs)
|
||||
* header is flushed.
|
||||
*/
|
||||
if (s->need_check_timer && timer_pending(s->need_check_timer)) {
|
||||
Coroutine *co;
|
||||
|
||||
qed_cancel_need_check_timer(s);
|
||||
qed_need_check_timer_entry(s);
|
||||
co = qemu_coroutine_create(qed_need_check_timer_entry, s);
|
||||
bdrv_inc_in_flight(bs);
|
||||
aio_co_enter(bdrv_get_aio_context(bs), co);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,7 +778,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv,
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
ret = bdrv_co_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1647,7 +1661,7 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_co_check = bdrv_qed_co_check,
|
||||
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
|
||||
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
|
||||
.bdrv_co_drain_begin = bdrv_qed_co_drain_begin,
|
||||
.bdrv_drain_begin = bdrv_qed_drain_begin,
|
||||
};
|
||||
|
||||
static void bdrv_qed_init(void)
|
||||
|
||||
+1
-1
@@ -433,7 +433,7 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
return bdrv_create_file(filename, opts, errp);
|
||||
return bdrv_co_create_file(filename, opts, errp);
|
||||
}
|
||||
|
||||
static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
@@ -374,9 +374,6 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||
s->orig_secondary_read_only = bdrv_is_read_only(secondary_disk->bs);
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_begin(hidden_disk->bs);
|
||||
bdrv_subtree_drained_begin(secondary_disk->bs);
|
||||
|
||||
if (s->orig_hidden_read_only) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
||||
@@ -401,9 +398,6 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_end(hidden_disk->bs);
|
||||
bdrv_subtree_drained_end(secondary_disk->bs);
|
||||
}
|
||||
|
||||
static void backup_job_cleanup(BlockDriverState *bs)
|
||||
|
||||
+16
-10
@@ -64,13 +64,16 @@ static int stream_prepare(Job *job)
|
||||
bdrv_cor_filter_drop(s->cor_filter_bs);
|
||||
s->cor_filter_bs = NULL;
|
||||
|
||||
bdrv_subtree_drained_begin(s->above_base);
|
||||
/*
|
||||
* bdrv_set_backing_hd() requires that unfiltered_bs is drained. Drain
|
||||
* already here and use bdrv_set_backing_hd_drained() instead because
|
||||
* the polling during drained_begin() might change the graph, and if we do
|
||||
* this only later, we may end up working with the wrong base node (or it
|
||||
* might even have gone away by the time we want to use it).
|
||||
*/
|
||||
bdrv_drained_begin(unfiltered_bs);
|
||||
|
||||
base = bdrv_filter_or_cow_bs(s->above_base);
|
||||
if (base) {
|
||||
bdrv_ref(base);
|
||||
}
|
||||
|
||||
unfiltered_base = bdrv_skip_filters(base);
|
||||
|
||||
if (bdrv_cow_child(unfiltered_bs)) {
|
||||
@@ -82,7 +85,13 @@ static int stream_prepare(Job *job)
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
|
||||
bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err);
|
||||
|
||||
/*
|
||||
* This call will do I/O, so the graph can change again from here on.
|
||||
* We have already completed the graph change, so we are not in danger
|
||||
* of operating on the wrong node any more if this happens.
|
||||
*/
|
||||
ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt, false);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
@@ -92,10 +101,7 @@ static int stream_prepare(Job *job)
|
||||
}
|
||||
|
||||
out:
|
||||
if (base) {
|
||||
bdrv_unref(base);
|
||||
}
|
||||
bdrv_subtree_drained_end(s->above_base);
|
||||
bdrv_drained_end(unfiltered_bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -214,7 +214,7 @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state)
|
||||
reopen_state->opaque = NULL;
|
||||
}
|
||||
|
||||
static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
|
||||
static void throttle_drain_begin(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) {
|
||||
@@ -222,7 +222,7 @@ static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
|
||||
static void throttle_drain_end(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
assert(tgm->io_limits_disabled);
|
||||
@@ -261,8 +261,8 @@ static BlockDriver bdrv_throttle = {
|
||||
.bdrv_reopen_commit = throttle_reopen_commit,
|
||||
.bdrv_reopen_abort = throttle_reopen_abort,
|
||||
|
||||
.bdrv_co_drain_begin = throttle_co_drain_begin,
|
||||
.bdrv_co_drain_end = throttle_co_drain_end,
|
||||
.bdrv_drain_begin = throttle_drain_begin,
|
||||
.bdrv_drain_end = throttle_drain_end,
|
||||
|
||||
.is_filter = true,
|
||||
.strong_runtime_opts = throttle_strong_runtime_opts,
|
||||
|
||||
+1
-1
@@ -934,7 +934,7 @@ static int coroutine_fn vdi_co_create_opts(BlockDriver *drv,
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
ret = bdrv_co_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user