block/stream: introduce a bottom node
The bottom node is the intermediate block device that has the base as its backing image. It is used instead of the base node while a block stream job is running to avoid dependency on the base that may change due to the parallel jobs. The change may take place due to a filter node as well that is inserted between the base and the intermediate bottom node. It occurs when the base node is the top one for another commit or stream job. After the introduction of the bottom node, don't freeze its backing child, that's the base, anymore. Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Alberto Garcia <berto@igalia.com> Message-id: 1559152576-281803-4-git-send-email-andrey.shinkevich@virtuozzo.com Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
96a07d5bf4
commit
c624b015bf
@ -31,7 +31,7 @@ enum {
|
|||||||
|
|
||||||
typedef struct StreamBlockJob {
|
typedef struct StreamBlockJob {
|
||||||
BlockJob common;
|
BlockJob common;
|
||||||
BlockDriverState *base;
|
BlockDriverState *bottom;
|
||||||
BlockdevOnError on_error;
|
BlockdevOnError on_error;
|
||||||
char *backing_file_str;
|
char *backing_file_str;
|
||||||
bool bs_read_only;
|
bool bs_read_only;
|
||||||
@ -54,7 +54,7 @@ static void stream_abort(Job *job)
|
|||||||
|
|
||||||
if (s->chain_frozen) {
|
if (s->chain_frozen) {
|
||||||
BlockJob *bjob = &s->common;
|
BlockJob *bjob = &s->common;
|
||||||
bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base);
|
bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,11 +63,11 @@ static int stream_prepare(Job *job)
|
|||||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||||
BlockJob *bjob = &s->common;
|
BlockJob *bjob = &s->common;
|
||||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
BlockDriverState *bs = blk_bs(bjob->blk);
|
||||||
BlockDriverState *base = s->base;
|
BlockDriverState *base = backing_bs(s->bottom);
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
bdrv_unfreeze_backing_chain(bs, base);
|
bdrv_unfreeze_backing_chain(bs, s->bottom);
|
||||||
s->chain_frozen = false;
|
s->chain_frozen = false;
|
||||||
|
|
||||||
if (bs->backing) {
|
if (bs->backing) {
|
||||||
@ -110,7 +110,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||||
BlockBackend *blk = s->common.blk;
|
BlockBackend *blk = s->common.blk;
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
BlockDriverState *base = s->base;
|
bool enable_cor = !backing_bs(s->bottom);
|
||||||
int64_t len;
|
int64_t len;
|
||||||
int64_t offset = 0;
|
int64_t offset = 0;
|
||||||
uint64_t delay_ns = 0;
|
uint64_t delay_ns = 0;
|
||||||
@ -119,7 +119,8 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
int64_t n = 0; /* bytes */
|
int64_t n = 0; /* bytes */
|
||||||
void *buf;
|
void *buf;
|
||||||
|
|
||||||
if (!bs->backing) {
|
if (bs == s->bottom) {
|
||||||
|
/* Nothing to stream */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
* backing chain since the copy-on-read operation does not take base into
|
* backing chain since the copy-on-read operation does not take base into
|
||||||
* account.
|
* account.
|
||||||
*/
|
*/
|
||||||
if (!base) {
|
if (enable_cor) {
|
||||||
bdrv_enable_copy_on_read(bs);
|
bdrv_enable_copy_on_read(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,9 +160,8 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
} else if (ret >= 0) {
|
} else if (ret >= 0) {
|
||||||
/* Copy if allocated in the intermediate images. Limit to the
|
/* Copy if allocated in the intermediate images. Limit to the
|
||||||
* known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */
|
* known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */
|
||||||
ret = bdrv_is_allocated_above(backing_bs(bs), base, false,
|
ret = bdrv_is_allocated_above(backing_bs(bs), s->bottom, true,
|
||||||
offset, n, &n);
|
offset, n, &n);
|
||||||
|
|
||||||
/* Finish early if end of backing file has been reached */
|
/* Finish early if end of backing file has been reached */
|
||||||
if (ret == 0 && n == 0) {
|
if (ret == 0 && n == 0) {
|
||||||
n = len - offset;
|
n = len - offset;
|
||||||
@ -198,7 +198,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!base) {
|
if (enable_cor) {
|
||||||
bdrv_disable_copy_on_read(bs);
|
bdrv_disable_copy_on_read(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,8 +230,10 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||||||
StreamBlockJob *s;
|
StreamBlockJob *s;
|
||||||
BlockDriverState *iter;
|
BlockDriverState *iter;
|
||||||
bool bs_read_only;
|
bool bs_read_only;
|
||||||
|
int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
|
||||||
|
BlockDriverState *bottom = bdrv_find_overlay(bs, base);
|
||||||
|
|
||||||
if (bdrv_freeze_backing_chain(bs, base, errp) < 0) {
|
if (bdrv_freeze_backing_chain(bs, bottom, errp) < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,10 +250,8 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||||||
* already have our own plans. Also don't allow resize as the image size is
|
* already have our own plans. Also don't allow resize as the image size is
|
||||||
* queried only at the job start and then cached. */
|
* queried only at the job start and then cached. */
|
||||||
s = block_job_create(job_id, &stream_job_driver, NULL, bs,
|
s = block_job_create(job_id, &stream_job_driver, NULL, bs,
|
||||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
basic_flags | BLK_PERM_GRAPH_MOD,
|
||||||
BLK_PERM_GRAPH_MOD,
|
basic_flags | BLK_PERM_WRITE,
|
||||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
|
||||||
BLK_PERM_WRITE,
|
|
||||||
speed, creation_flags, NULL, NULL, errp);
|
speed, creation_flags, NULL, NULL, errp);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -259,15 +259,18 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||||||
|
|
||||||
/* Block all intermediate nodes between bs and base, because they will
|
/* Block all intermediate nodes between bs and base, because they will
|
||||||
* disappear from the chain after this operation. The streaming job reads
|
* disappear from the chain after this operation. The streaming job reads
|
||||||
* every block only once, assuming that it doesn't change, so block writes
|
* every block only once, assuming that it doesn't change, so forbid writes
|
||||||
* and resizes. */
|
* and resizes. Reassign the base node pointer because the backing BS of the
|
||||||
|
* bottom node might change after the call to bdrv_reopen_set_read_only()
|
||||||
|
* due to parallel block jobs running.
|
||||||
|
*/
|
||||||
|
base = backing_bs(bottom);
|
||||||
for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
|
for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
|
||||||
block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
|
block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
|
||||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED,
|
basic_flags, &error_abort);
|
||||||
&error_abort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s->base = base;
|
s->bottom = bottom;
|
||||||
s->backing_file_str = g_strdup(backing_file_str);
|
s->backing_file_str = g_strdup(backing_file_str);
|
||||||
s->bs_read_only = bs_read_only;
|
s->bs_read_only = bs_read_only;
|
||||||
s->chain_frozen = true;
|
s->chain_frozen = true;
|
||||||
|
@ -866,9 +866,9 @@ class TestBlockdevReopen(iotests.QMPTestCase):
|
|||||||
auto_finalize = False)
|
auto_finalize = False)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
# We can't remove hd2 while the stream job is ongoing
|
# We can remove hd2 while the stream job is ongoing
|
||||||
opts['backing']['backing'] = None
|
opts['backing']['backing'] = None
|
||||||
self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
|
self.reopen(opts, {})
|
||||||
|
|
||||||
# We can't remove hd1 while the stream job is ongoing
|
# We can't remove hd1 while the stream job is ongoing
|
||||||
opts['backing'] = None
|
opts['backing'] = None
|
||||||
|
Loading…
Reference in New Issue
Block a user