mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches: - Active mirror (blockdev-mirror copy-mode=write-blocking) - bdrv_drain_*() fixes and test cases - Fix crash with scsi-hd and drive_del # gpg: Signature made Mon 18 Jun 2018 17:44:10 BST # gpg: using RSA key 7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (35 commits) iotests: Add test for active mirroring block/mirror: Add copy mode QAPI interface block/mirror: Add active mirroring job: Add job_progress_increase_remaining() block/mirror: Add MirrorBDSOpaque block/dirty-bitmap: Add bdrv_dirty_iter_next_area test-hbitmap: Add non-advancing iter_next tests hbitmap: Add @advance param to hbitmap_iter_next() block: Generalize should_update_child() rule block/mirror: Use source as a BdrvChild block/mirror: Wait for in-flight op conflicts block/mirror: Use CoQueue to wait on in-flight ops block/mirror: Convert to coroutines block/mirror: Pull out mirror_perform() block: fix QEMU crash with scsi-hd and drive_del test-bdrv-drain: Test graph changes in drain_all section block: Allow graph changes in bdrv_drain_all_begin/end sections block: ignore_bds_parents parameter for drain functions block: Move bdrv_drain_all_begin() out of coroutine context block: Allow AIO_WAIT_WHILE with NULL ctx ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -333,6 +333,10 @@ BlockDriverState *bdrv_new(void)
|
||||
|
||||
qemu_co_queue_init(&bs->flush_queue);
|
||||
|
||||
for (i = 0; i < bdrv_drain_all_count; i++) {
|
||||
bdrv_drained_begin(bs);
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list);
|
||||
|
||||
return bs;
|
||||
@@ -818,7 +822,13 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
|
||||
static void bdrv_child_cb_drained_begin(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
bdrv_drained_begin(bs);
|
||||
bdrv_do_drained_begin_quiesce(bs, NULL, false);
|
||||
}
|
||||
|
||||
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
return bdrv_drain_poll(bs, false, NULL, false);
|
||||
}
|
||||
|
||||
static void bdrv_child_cb_drained_end(BdrvChild *child)
|
||||
@@ -902,9 +912,11 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
|
||||
}
|
||||
|
||||
const BdrvChildRole child_file = {
|
||||
.parent_is_bds = true,
|
||||
.get_parent_desc = bdrv_child_get_parent_desc,
|
||||
.inherit_options = bdrv_inherited_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_poll = bdrv_child_cb_drained_poll,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
@@ -926,9 +938,11 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options,
|
||||
}
|
||||
|
||||
const BdrvChildRole child_format = {
|
||||
.parent_is_bds = true,
|
||||
.get_parent_desc = bdrv_child_get_parent_desc,
|
||||
.inherit_options = bdrv_inherited_fmt_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_poll = bdrv_child_cb_drained_poll,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
@@ -1043,11 +1057,13 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
|
||||
}
|
||||
|
||||
const BdrvChildRole child_backing = {
|
||||
.parent_is_bds = true,
|
||||
.get_parent_desc = bdrv_child_get_parent_desc,
|
||||
.attach = bdrv_backing_attach,
|
||||
.detach = bdrv_backing_detach,
|
||||
.inherit_options = bdrv_backing_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_poll = bdrv_child_cb_drained_poll,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
.update_filename = bdrv_backing_update_filename,
|
||||
@@ -1152,7 +1168,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
|
||||
int open_flags, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
bdrv_assign_node_name(bs, node_name, &local_err);
|
||||
if (local_err) {
|
||||
@@ -1200,6 +1216,12 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
|
||||
assert(bdrv_min_mem_align(bs) != 0);
|
||||
assert(is_power_of_2(bs->bl.request_alignment));
|
||||
|
||||
for (i = 0; i < bs->quiesce_counter; i++) {
|
||||
if (drv->bdrv_co_drain_begin) {
|
||||
drv->bdrv_co_drain_begin(bs);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
open_failed:
|
||||
bs->drv = NULL;
|
||||
@@ -2021,7 +2043,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
child->role->detach(child);
|
||||
}
|
||||
if (old_bs->quiesce_counter && child->role->drained_end) {
|
||||
for (i = 0; i < old_bs->quiesce_counter; i++) {
|
||||
int num = old_bs->quiesce_counter;
|
||||
if (child->role->parent_is_bds) {
|
||||
num -= bdrv_drain_all_count;
|
||||
}
|
||||
assert(num >= 0);
|
||||
for (i = 0; i < num; i++) {
|
||||
child->role->drained_end(child);
|
||||
}
|
||||
}
|
||||
@@ -2033,7 +2060,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
if (new_bs) {
|
||||
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
|
||||
if (new_bs->quiesce_counter && child->role->drained_begin) {
|
||||
for (i = 0; i < new_bs->quiesce_counter; i++) {
|
||||
int num = new_bs->quiesce_counter;
|
||||
if (child->role->parent_is_bds) {
|
||||
num -= bdrv_drain_all_count;
|
||||
}
|
||||
assert(num >= 0);
|
||||
for (i = 0; i < num; i++) {
|
||||
child->role->drained_begin(child);
|
||||
}
|
||||
}
|
||||
@@ -3395,16 +3427,39 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c->role == &child_backing) {
|
||||
/* If @from is a backing file of @to, ignore the child to avoid
|
||||
* creating a loop. We only want to change the pointer of other
|
||||
* parents. */
|
||||
QLIST_FOREACH(to_c, &to->children, next) {
|
||||
if (to_c == c) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (to_c) {
|
||||
/* If the child @c belongs to the BDS @to, replacing the current
|
||||
* c->bs by @to would mean to create a loop.
|
||||
*
|
||||
* Such a case occurs when appending a BDS to a backing chain.
|
||||
* For instance, imagine the following chain:
|
||||
*
|
||||
* guest device -> node A -> further backing chain...
|
||||
*
|
||||
* Now we create a new BDS B which we want to put on top of this
|
||||
* chain, so we first attach A as its backing node:
|
||||
*
|
||||
* node B
|
||||
* |
|
||||
* v
|
||||
* guest device -> node A -> further backing chain...
|
||||
*
|
||||
* Finally we want to replace A by B. When doing that, we want to
|
||||
* replace all pointers to A by pointers to B -- except for the
|
||||
* pointer from B because (1) that would create a loop, and (2)
|
||||
* that pointer should simply stay intact:
|
||||
*
|
||||
* guest device -> node B
|
||||
* |
|
||||
* v
|
||||
* node A -> further backing chain...
|
||||
*
|
||||
* In general, when replacing a node A (c->bs) by a node B (@to),
|
||||
* if A is a child of B, that means we cannot replace A by B there
|
||||
* because that would create a loop. Silently detaching A from B
|
||||
* is also not really an option. So overall just leaving A in
|
||||
* place there is the most sensible choice. */
|
||||
QLIST_FOREACH(to_c, &to->children, next) {
|
||||
if (to_c == c) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3430,6 +3485,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
||||
|
||||
/* Put all parents into @list and calculate their cumulative permissions */
|
||||
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
|
||||
assert(c->bs == from);
|
||||
if (!should_update_child(c, to)) {
|
||||
continue;
|
||||
}
|
||||
@@ -4037,6 +4093,14 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs)
|
||||
return QTAILQ_NEXT(bs, node_list);
|
||||
}
|
||||
|
||||
BlockDriverState *bdrv_next_all_states(BlockDriverState *bs)
|
||||
{
|
||||
if (!bs) {
|
||||
return QTAILQ_FIRST(&all_bdrv_states);
|
||||
}
|
||||
return QTAILQ_NEXT(bs, bs_list);
|
||||
}
|
||||
|
||||
const char *bdrv_get_node_name(const BlockDriverState *bs)
|
||||
{
|
||||
return bs->node_name;
|
||||
@@ -4948,7 +5012,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_disable_external(ctx);
|
||||
bdrv_parent_drained_begin(bs, NULL);
|
||||
bdrv_parent_drained_begin(bs, NULL, false);
|
||||
bdrv_drain(bs); /* ensure there are no in-flight requests */
|
||||
|
||||
while (aio_poll(ctx, false)) {
|
||||
@@ -4962,7 +5026,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
*/
|
||||
aio_context_acquire(new_context);
|
||||
bdrv_attach_aio_context(bs, new_context);
|
||||
bdrv_parent_drained_end(bs, NULL);
|
||||
bdrv_parent_drained_end(bs, NULL, false);
|
||||
aio_enable_external(ctx);
|
||||
aio_context_release(new_context);
|
||||
}
|
||||
|
||||
+1
-1
@@ -354,7 +354,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
HBitmapIter hbi;
|
||||
|
||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||
while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
return 0;
|
||||
|
||||
@@ -767,6 +767,11 @@ void blk_remove_bs(BlockBackend *blk)
|
||||
|
||||
blk_update_root_state(blk);
|
||||
|
||||
/* bdrv_root_unref_child() will cause blk->root to become stale and may
|
||||
* switch to a completion coroutine later on. Let's drain all I/O here
|
||||
* to avoid that and a potential QEMU crash.
|
||||
*/
|
||||
blk_drain(blk);
|
||||
bdrv_root_unref_child(blk->root);
|
||||
blk->root = NULL;
|
||||
}
|
||||
|
||||
+56
-1
@@ -519,7 +519,62 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
|
||||
|
||||
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
||||
{
|
||||
return hbitmap_iter_next(&iter->hbi);
|
||||
return hbitmap_iter_next(&iter->hbi, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next consecutively dirty area in the dirty bitmap
|
||||
* belonging to the given iterator @iter.
|
||||
*
|
||||
* @max_offset: Maximum value that may be returned for
|
||||
* *offset + *bytes
|
||||
* @offset: Will contain the start offset of the next dirty area
|
||||
* @bytes: Will contain the length of the next dirty area
|
||||
*
|
||||
* Returns: True if a dirty area could be found before max_offset
|
||||
* (which means that *offset and *bytes then contain valid
|
||||
* values), false otherwise.
|
||||
*
|
||||
* Note that @iter is never advanced if false is returned. If an area
|
||||
* is found (which means that true is returned), it will be advanced
|
||||
* past that area.
|
||||
*/
|
||||
bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset,
|
||||
uint64_t *offset, int *bytes)
|
||||
{
|
||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap);
|
||||
uint64_t gran_max_offset;
|
||||
int64_t ret;
|
||||
int size;
|
||||
|
||||
if (max_offset == iter->bitmap->size) {
|
||||
/* If max_offset points to the image end, round it up by the
|
||||
* bitmap granularity */
|
||||
gran_max_offset = ROUND_UP(max_offset, granularity);
|
||||
} else {
|
||||
gran_max_offset = max_offset;
|
||||
}
|
||||
|
||||
ret = hbitmap_iter_next(&iter->hbi, false);
|
||||
if (ret < 0 || ret + granularity > gran_max_offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*offset = ret;
|
||||
size = 0;
|
||||
|
||||
assert(granularity <= INT_MAX);
|
||||
|
||||
do {
|
||||
/* Advance iterator */
|
||||
ret = hbitmap_iter_next(&iter->hbi, true);
|
||||
size += granularity;
|
||||
} while (ret + granularity <= gran_max_offset &&
|
||||
hbitmap_iter_next(&iter->hbi, false) == ret + granularity &&
|
||||
size <= INT_MAX - granularity);
|
||||
|
||||
*bytes = MIN(size, max_offset - *offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
|
||||
+209
-121
File diff suppressed because it is too large
Load Diff
+494
-119
File diff suppressed because it is too large
Load Diff
@@ -3134,6 +3134,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_vvfat_qcow = {
|
||||
.parent_is_bds = true,
|
||||
.inherit_options = vvfat_qcow_options,
|
||||
};
|
||||
|
||||
|
||||
+8
-1
@@ -3586,6 +3586,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
bool has_unmap, bool unmap,
|
||||
bool has_filter_node_name,
|
||||
const char *filter_node_name,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
Error **errp)
|
||||
{
|
||||
|
||||
@@ -3610,6 +3611,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
if (!has_filter_node_name) {
|
||||
filter_node_name = NULL;
|
||||
}
|
||||
if (!has_copy_mode) {
|
||||
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
|
||||
}
|
||||
|
||||
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
||||
@@ -3640,7 +3644,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync, backing_mode,
|
||||
on_source_error, on_target_error, unmap, filter_node_name,
|
||||
errp);
|
||||
copy_mode, errp);
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
@@ -3786,6 +3790,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
arg->has_on_target_error, arg->on_target_error,
|
||||
arg->has_unmap, arg->unmap,
|
||||
false, NULL,
|
||||
arg->has_copy_mode, arg->copy_mode,
|
||||
&local_err);
|
||||
bdrv_unref(target_bs);
|
||||
error_propagate(errp, local_err);
|
||||
@@ -3806,6 +3811,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
||||
BlockdevOnError on_target_error,
|
||||
bool has_filter_node_name,
|
||||
const char *filter_node_name,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -3838,6 +3844,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
||||
has_on_target_error, on_target_error,
|
||||
true, true,
|
||||
has_filter_node_name, filter_node_name,
|
||||
has_copy_mode, copy_mode,
|
||||
&local_err);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
|
||||
+23
@@ -155,6 +155,28 @@ static void child_job_drained_begin(BdrvChild *c)
|
||||
job_pause(&job->job);
|
||||
}
|
||||
|
||||
static bool child_job_drained_poll(BdrvChild *c)
|
||||
{
|
||||
BlockJob *bjob = c->opaque;
|
||||
Job *job = &bjob->job;
|
||||
const BlockJobDriver *drv = block_job_driver(bjob);
|
||||
|
||||
/* An inactive or completed job doesn't have any pending requests. Jobs
|
||||
* with !job->busy are either already paused or have a pause point after
|
||||
* being reentered, so no job driver code will run before they pause. */
|
||||
if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Otherwise, assume that it isn't fully stopped yet, but allow the job to
|
||||
* override this assumption. */
|
||||
if (drv->drained_poll) {
|
||||
return drv->drained_poll(bjob);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void child_job_drained_end(BdrvChild *c)
|
||||
{
|
||||
BlockJob *job = c->opaque;
|
||||
@@ -164,6 +186,7 @@ static void child_job_drained_end(BdrvChild *c)
|
||||
static const BdrvChildRole child_job = {
|
||||
.get_parent_desc = child_job_get_parent_desc,
|
||||
.drained_begin = child_job_drained_begin,
|
||||
.drained_poll = child_job_drained_poll,
|
||||
.drained_end = child_job_drained_end,
|
||||
.stay_at_node = true,
|
||||
};
|
||||
|
||||
+13
-14
@@ -57,7 +57,8 @@ typedef struct {
|
||||
/**
|
||||
* AIO_WAIT_WHILE:
|
||||
* @wait: the aio wait object
|
||||
* @ctx: the aio context
|
||||
* @ctx: the aio context, or NULL if multiple aio contexts (for which the
|
||||
* caller does not hold a lock) are involved in the polling condition.
|
||||
* @cond: wait while this conditional expression is true
|
||||
*
|
||||
* Wait while a condition is true. Use this to implement synchronous
|
||||
@@ -73,29 +74,27 @@ typedef struct {
|
||||
*/
|
||||
#define AIO_WAIT_WHILE(wait, ctx, cond) ({ \
|
||||
bool waited_ = false; \
|
||||
bool busy_ = true; \
|
||||
AioWait *wait_ = (wait); \
|
||||
AioContext *ctx_ = (ctx); \
|
||||
if (in_aio_context_home_thread(ctx_)) { \
|
||||
while ((cond) || busy_) { \
|
||||
busy_ = aio_poll(ctx_, (cond)); \
|
||||
waited_ |= !!(cond) | busy_; \
|
||||
if (ctx_ && in_aio_context_home_thread(ctx_)) { \
|
||||
while ((cond)) { \
|
||||
aio_poll(ctx_, true); \
|
||||
waited_ = true; \
|
||||
} \
|
||||
} else { \
|
||||
assert(qemu_get_current_aio_context() == \
|
||||
qemu_get_aio_context()); \
|
||||
/* Increment wait_->num_waiters before evaluating cond. */ \
|
||||
atomic_inc(&wait_->num_waiters); \
|
||||
while (busy_) { \
|
||||
if ((cond)) { \
|
||||
waited_ = busy_ = true; \
|
||||
while ((cond)) { \
|
||||
if (ctx_) { \
|
||||
aio_context_release(ctx_); \
|
||||
aio_poll(qemu_get_aio_context(), true); \
|
||||
aio_context_acquire(ctx_); \
|
||||
} else { \
|
||||
busy_ = aio_poll(ctx_, false); \
|
||||
waited_ |= busy_; \
|
||||
} \
|
||||
aio_poll(qemu_get_aio_context(), true); \
|
||||
if (ctx_) { \
|
||||
aio_context_acquire(ctx_); \
|
||||
} \
|
||||
waited_ = true; \
|
||||
} \
|
||||
atomic_dec(&wait_->num_waiters); \
|
||||
} \
|
||||
|
||||
+29
-2
@@ -421,6 +421,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
|
||||
Error **errp);
|
||||
bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base);
|
||||
BlockDriverState *bdrv_next_node(BlockDriverState *bs);
|
||||
BlockDriverState *bdrv_next_all_states(BlockDriverState *bs);
|
||||
|
||||
typedef struct BdrvNextIterator {
|
||||
enum {
|
||||
@@ -557,7 +558,8 @@ void bdrv_io_unplug(BlockDriverState *bs);
|
||||
* Begin a quiesced section of all users of @bs. This is part of
|
||||
* bdrv_drained_begin.
|
||||
*/
|
||||
void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
|
||||
void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
|
||||
bool ignore_bds_parents);
|
||||
|
||||
/**
|
||||
* bdrv_parent_drained_end:
|
||||
@@ -565,7 +567,23 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
|
||||
* End a quiesced section of all users of @bs. This is part of
|
||||
* bdrv_drained_end.
|
||||
*/
|
||||
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
|
||||
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
|
||||
bool ignore_bds_parents);
|
||||
|
||||
/**
|
||||
* bdrv_drain_poll:
|
||||
*
|
||||
* Poll for pending requests in @bs, its parents (except for @ignore_parent),
|
||||
* and if @recursive is true its children as well (used for subtree drain).
|
||||
*
|
||||
* If @ignore_bds_parents is true, parents that are BlockDriverStates must
|
||||
* ignore the drain request because they will be drained separately (used for
|
||||
* drain_all).
|
||||
*
|
||||
* This is part of bdrv_drained_begin.
|
||||
*/
|
||||
bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *ignore_parent, bool ignore_bds_parents);
|
||||
|
||||
/**
|
||||
* bdrv_drained_begin:
|
||||
@@ -579,6 +597,15 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
|
||||
*/
|
||||
void bdrv_drained_begin(BlockDriverState *bs);
|
||||
|
||||
/**
|
||||
* bdrv_do_drained_begin_quiesce:
|
||||
*
|
||||
* Quiesces a BDS like bdrv_drained_begin(), but does not wait for already
|
||||
* running requests to complete.
|
||||
*/
|
||||
void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
|
||||
BdrvChild *parent, bool ignore_bds_parents);
|
||||
|
||||
/**
|
||||
* Like bdrv_drained_begin, but recursively begins a quiesced section for
|
||||
* exclusive access to all child nodes as well.
|
||||
|
||||
@@ -577,6 +577,12 @@ struct BdrvChildRole {
|
||||
* points to. */
|
||||
bool stay_at_node;
|
||||
|
||||
/* If true, the parent is a BlockDriverState and bdrv_next_all_states()
|
||||
* will return it. This information is used for drain_all, where every node
|
||||
* will be drained separately, so the drain only needs to be propagated to
|
||||
* non-BDS parents. */
|
||||
bool parent_is_bds;
|
||||
|
||||
void (*inherit_options)(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options);
|
||||
|
||||
@@ -605,6 +611,13 @@ struct BdrvChildRole {
|
||||
void (*drained_begin)(BdrvChild *child);
|
||||
void (*drained_end)(BdrvChild *child);
|
||||
|
||||
/*
|
||||
* Returns whether the parent has pending requests for the child. This
|
||||
* callback is polled after .drained_begin() has been called until all
|
||||
* activity on the child has stopped.
|
||||
*/
|
||||
bool (*drained_poll)(BdrvChild *child);
|
||||
|
||||
/* Notifies the parent that the child has been activated/inactivated (e.g.
|
||||
* when migration is completing) and it can start/stop requesting
|
||||
* permissions and doing I/O on it. */
|
||||
@@ -841,6 +854,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
|
||||
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
|
||||
BdrvRequestFlags flags);
|
||||
|
||||
extern unsigned int bdrv_drain_all_count;
|
||||
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
|
||||
void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
|
||||
|
||||
@@ -1017,6 +1031,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
* @filter_node_name: The node name that should be assigned to the filter
|
||||
* driver that the mirror job inserts into the graph above @bs. NULL means that
|
||||
* a node name should be autogenerated.
|
||||
* @copy_mode: When to trigger writes to the target.
|
||||
* @errp: Error object.
|
||||
*
|
||||
* Start a mirroring operation on @bs. Clusters that are allocated
|
||||
@@ -1030,7 +1045,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap, const char *filter_node_name, Error **errp);
|
||||
bool unmap, const char *filter_node_name,
|
||||
MirrorCopyMode copy_mode, Error **errp);
|
||||
|
||||
/*
|
||||
* backup_job_create:
|
||||
|
||||
@@ -38,6 +38,14 @@ struct BlockJobDriver {
|
||||
/** Generic JobDriver callbacks and settings */
|
||||
JobDriver job_driver;
|
||||
|
||||
/*
|
||||
* Returns whether the job has pending requests for the child or will
|
||||
* submit new requests before the next pause point. This callback is polled
|
||||
* in the context of draining a job node after requesting that the job be
|
||||
* paused, until all activity on the child has stopped.
|
||||
*/
|
||||
bool (*drained_poll)(BlockJob *job);
|
||||
|
||||
/*
|
||||
* If the callback is not NULL, it will be invoked before the job is
|
||||
* resumed in a new AioContext. This is the place to move any resources
|
||||
|
||||
@@ -82,6 +82,8 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
int64_t offset, int64_t bytes);
|
||||
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);
|
||||
bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset,
|
||||
uint64_t *offset, int *bytes);
|
||||
void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset);
|
||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
|
||||
int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap);
|
||||
|
||||
@@ -324,11 +324,14 @@ void hbitmap_free_meta(HBitmap *hb);
|
||||
/**
|
||||
* hbitmap_iter_next:
|
||||
* @hbi: HBitmapIter to operate on.
|
||||
* @advance: If true, advance the iterator. Otherwise, the next call
|
||||
* of this function will return the same result (if that
|
||||
* position is still dirty).
|
||||
*
|
||||
* Return the next bit that is set in @hbi's associated HBitmap,
|
||||
* or -1 if all remaining bits are zero.
|
||||
*/
|
||||
int64_t hbitmap_iter_next(HBitmapIter *hbi);
|
||||
int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance);
|
||||
|
||||
/**
|
||||
* hbitmap_iter_next_word:
|
||||
|
||||
@@ -335,6 +335,21 @@ void job_progress_update(Job *job, uint64_t done);
|
||||
*/
|
||||
void job_progress_set_remaining(Job *job, uint64_t remaining);
|
||||
|
||||
/**
|
||||
* @job: The job whose expected progress end value is updated
|
||||
* @delta: Value which is to be added to the current expected end
|
||||
* value
|
||||
*
|
||||
* Increases the expected end value of the progress counter of a job.
|
||||
* This is useful for parenthesis operations: If a job has to
|
||||
* conditionally perform a high-priority operation as part of its
|
||||
* progress, it calls this function with the expected operation's
|
||||
* length before, and job_progress_update() afterwards.
|
||||
* (So the operation acts as a parenthesis in regards to the main job
|
||||
* operation running in background.)
|
||||
*/
|
||||
void job_progress_increase_remaining(Job *job, uint64_t delta);
|
||||
|
||||
/** To be called when a cancelled job is finalised. */
|
||||
void job_event_cancelled(Job *job);
|
||||
|
||||
|
||||
@@ -385,6 +385,11 @@ void job_progress_set_remaining(Job *job, uint64_t remaining)
|
||||
job->progress_total = job->progress_current + remaining;
|
||||
}
|
||||
|
||||
void job_progress_increase_remaining(Job *job, uint64_t delta)
|
||||
{
|
||||
job->progress_total += delta;
|
||||
}
|
||||
|
||||
void job_event_cancelled(Job *job)
|
||||
{
|
||||
notifier_list_notify(&job->on_finalize_cancelled, job);
|
||||
|
||||
+27
-2
@@ -1050,6 +1050,24 @@
|
||||
{ 'enum': 'MirrorSyncMode',
|
||||
'data': ['top', 'full', 'none', 'incremental'] }
|
||||
|
||||
##
|
||||
# @MirrorCopyMode:
|
||||
#
|
||||
# An enumeration whose values tell the mirror block job when to
|
||||
# trigger writes to the target.
|
||||
#
|
||||
# @background: copy data in background only.
|
||||
#
|
||||
# @write-blocking: when data is written to the source, write it
|
||||
# (synchronously) to the target as well. In
|
||||
# addition, data is copied in background just like in
|
||||
# @background mode.
|
||||
#
|
||||
# Since: 3.0
|
||||
##
|
||||
{ 'enum': 'MirrorCopyMode',
|
||||
'data': ['background', 'write-blocking'] }
|
||||
|
||||
##
|
||||
# @BlockJobInfo:
|
||||
#
|
||||
@@ -1692,6 +1710,9 @@
|
||||
# written. Both will result in identical contents.
|
||||
# Default is true. (Since 2.4)
|
||||
#
|
||||
# @copy-mode: when to copy data to the destination; defaults to 'background'
|
||||
# (Since: 3.0)
|
||||
#
|
||||
# Since: 1.3
|
||||
##
|
||||
{ 'struct': 'DriveMirror',
|
||||
@@ -1701,7 +1722,7 @@
|
||||
'*speed': 'int', '*granularity': 'uint32',
|
||||
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
|
||||
'*on-target-error': 'BlockdevOnError',
|
||||
'*unmap': 'bool' } }
|
||||
'*unmap': 'bool', '*copy-mode': 'MirrorCopyMode' } }
|
||||
|
||||
##
|
||||
# @BlockDirtyBitmap:
|
||||
@@ -1964,6 +1985,9 @@
|
||||
# above @device. If this option is not given, a node name is
|
||||
# autogenerated. (Since: 2.9)
|
||||
#
|
||||
# @copy-mode: when to copy data to the destination; defaults to 'background'
|
||||
# (Since: 3.0)
|
||||
#
|
||||
# Returns: nothing on success.
|
||||
#
|
||||
# Since: 2.6
|
||||
@@ -1984,7 +2008,8 @@
|
||||
'*speed': 'int', '*granularity': 'uint32',
|
||||
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
|
||||
'*on-target-error': 'BlockdevOnError',
|
||||
'*filter-node-name': 'str' } }
|
||||
'*filter-node-name': 'str',
|
||||
'*copy-mode': 'MirrorCopyMode' } }
|
||||
|
||||
##
|
||||
# @block_set_io_throttle:
|
||||
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Tests for active mirroring
|
||||
#
|
||||
# Copyright (C) 2018 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import iotests
|
||||
from iotests import qemu_img
|
||||
|
||||
source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt)
|
||||
target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
|
||||
|
||||
class TestActiveMirror(iotests.QMPTestCase):
|
||||
image_len = 128 * 1024 * 1024 # MB
|
||||
potential_writes_in_flight = True
|
||||
|
||||
def setUp(self):
|
||||
qemu_img('create', '-f', iotests.imgfmt, source_img, '128M')
|
||||
qemu_img('create', '-f', iotests.imgfmt, target_img, '128M')
|
||||
|
||||
blk_source = {'id': 'source',
|
||||
'if': 'none',
|
||||
'node-name': 'source-node',
|
||||
'driver': iotests.imgfmt,
|
||||
'file': {'driver': 'file',
|
||||
'filename': source_img}}
|
||||
|
||||
blk_target = {'node-name': 'target-node',
|
||||
'driver': iotests.imgfmt,
|
||||
'file': {'driver': 'file',
|
||||
'filename': target_img}}
|
||||
|
||||
self.vm = iotests.VM()
|
||||
self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source))
|
||||
self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target))
|
||||
self.vm.add_device('virtio-blk,drive=source')
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown()
|
||||
|
||||
if not self.potential_writes_in_flight:
|
||||
self.assertTrue(iotests.compare_images(source_img, target_img),
|
||||
'mirror target does not match source')
|
||||
|
||||
os.remove(source_img)
|
||||
os.remove(target_img)
|
||||
|
||||
def doActiveIO(self, sync_source_and_target):
|
||||
# Fill the source image
|
||||
self.vm.hmp_qemu_io('source',
|
||||
'write -P 1 0 %i' % self.image_len);
|
||||
|
||||
# Start some background requests
|
||||
for offset in range(1 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024):
|
||||
self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset)
|
||||
for offset in range(2 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024):
|
||||
self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
|
||||
|
||||
# Start the block job
|
||||
result = self.vm.qmp('blockdev-mirror',
|
||||
job_id='mirror',
|
||||
filter_node_name='mirror-node',
|
||||
device='source-node',
|
||||
target='target-node',
|
||||
sync='full',
|
||||
copy_mode='write-blocking')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
# Start some more requests
|
||||
for offset in range(3 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024):
|
||||
self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset)
|
||||
for offset in range(4 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024):
|
||||
self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
|
||||
|
||||
# Wait for the READY event
|
||||
self.wait_ready(drive='mirror')
|
||||
|
||||
# Now start some final requests; all of these (which land on
|
||||
# the source) should be settled using the active mechanism.
|
||||
# The mirror code itself asserts that the source BDS's dirty
|
||||
# bitmap will stay clean between READY and COMPLETED.
|
||||
for offset in range(5 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024):
|
||||
self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset)
|
||||
for offset in range(6 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024):
|
||||
self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
|
||||
|
||||
if sync_source_and_target:
|
||||
# If source and target should be in sync after the mirror,
|
||||
# we have to flush before completion
|
||||
self.vm.hmp_qemu_io('source', 'aio_flush')
|
||||
self.potential_writes_in_flight = False
|
||||
|
||||
self.complete_and_wait(drive='mirror', wait_ready=False)
|
||||
|
||||
def testActiveIO(self):
|
||||
self.doActiveIO(False)
|
||||
|
||||
def testActiveIOFlushed(self):
|
||||
self.doActiveIO(True)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2', 'raw'])
|
||||
@@ -0,0 +1,5 @@
|
||||
..
|
||||
----------------------------------------------------------------------
|
||||
Ran 2 tests
|
||||
|
||||
OK
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user