From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>,
qemu-block@nongnu.org
Cc: kwolf@redhat.com, armbru@redhat.com, qemu-devel@nongnu.org,
den@openvz.org, mreitz@redhat.com, jsnow@redhat.com
Subject: Re: [PATCH v7 4/4] block: apply COR-filter to block-stream jobs
Date: Mon, 24 Aug 2020 14:30:00 +0300 [thread overview]
Message-ID: <2eef369e-a79d-57c9-f8c8-40be9c3aaa2c@virtuozzo.com> (raw)
In-Reply-To: <1598257914-887267-5-git-send-email-andrey.shinkevich@virtuozzo.com>
24.08.2020 11:31, Andrey Shinkevich wrote:
> This patch completes the series with the COR-filter insertion for
> block-stream operations. Adding the filter makes it possible for copied
> regions to be discarded in backing files during the block-stream job,
> what will reduce the disk overuse.
> The COR-filter insertion incurs changes in the iotests case
> 245:test_block_stream_4 that reopens the backing chain during a
> block-stream job. There are changes in the iotests #030 as well.
> The iotests case 030:test_stream_parallel was deleted due to multiple
> conflicts between the concurrent job operations over the same backing
> chain. The base backing node for one job is the top node for another
> job. It may change due to the filter node inserted into the backing
> chain while both jobs are running. Another issue is that the parts of
> the backing chain are being frozen by the running job and may not be
> changed by the concurrent job when needed. The concept of the parallel
> jobs with common nodes is considered vital no more.
>
> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
> ---
> block/stream.c | 58 +++++++++++++++++++++++++++++++++++-----------
> tests/qemu-iotests/030 | 50 ++++-----------------------------------
> tests/qemu-iotests/030.out | 4 ++--
> tests/qemu-iotests/245 | 19 +++++++++++----
> 4 files changed, 65 insertions(+), 66 deletions(-)
>
> diff --git a/block/stream.c b/block/stream.c
> index 8bf6b6d..e927fed 100644
> --- a/block/stream.c
> +++ b/block/stream.c
> @@ -19,6 +19,7 @@
> #include "qapi/qmp/qerror.h"
> #include "qemu/ratelimit.h"
> #include "sysemu/block-backend.h"
> +#include "block/copy-on-read.h"
>
> enum {
> /*
> @@ -33,6 +34,8 @@ typedef struct StreamBlockJob {
> BlockJob common;
> BlockDriverState *base_overlay; /* COW overlay (stream from this) */
> BlockDriverState *above_base; /* Node directly above the base */
> + BlockDriverState *cor_filter_bs;
> + BlockDriverState *target_bs;
> BlockdevOnError on_error;
> char *backing_file_str;
> bool bs_read_only;
> @@ -53,22 +56,20 @@ static void stream_abort(Job *job)
> StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
>
> if (s->chain_frozen) {
> - BlockJob *bjob = &s->common;
> - bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->above_base);
> + bdrv_unfreeze_backing_chain(s->cor_filter_bs, s->above_base);
> }
> }
>
> static int stream_prepare(Job *job)
> {
> StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
> - BlockJob *bjob = &s->common;
> - BlockDriverState *bs = blk_bs(bjob->blk);
> + BlockDriverState *bs = s->target_bs;
> BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
variable bs is used only here, we can directly use s->target_bs instead
> BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base);
> Error *local_err = NULL;
> int ret = 0;
>
> - bdrv_unfreeze_backing_chain(bs, s->above_base);
> + bdrv_unfreeze_backing_chain(s->cor_filter_bs, s->above_base);
> s->chain_frozen = false;
>
> if (bdrv_cow_child(unfiltered_bs)) {
> @@ -94,7 +95,9 @@ static void stream_clean(Job *job)
> {
> StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
> BlockJob *bjob = &s->common;
> - BlockDriverState *bs = blk_bs(bjob->blk);
> + BlockDriverState *bs = s->target_bs;
Here as well, I'd drop extra local bs variable
> +
> + bdrv_cor_filter_drop(s->cor_filter_bs);
>
> /* Reopen the image back in read-only mode if necessary */
> if (s->bs_read_only) {
> @@ -110,7 +113,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
> {
> StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
> BlockBackend *blk = s->common.blk;
> - BlockDriverState *bs = blk_bs(blk);
> + BlockDriverState *bs = s->target_bs;
Now we have both bdrv_enable_copy_on_read(bs) and cor-filter, which doesn't seem correct. Also, we'll need "base" parameter for cor filter to not copy-on-read extra chunks.
> BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
> bool enable_cor = !bdrv_cow_child(s->base_overlay);
> int64_t len;
> @@ -231,6 +234,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
> int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
> BlockDriverState *base_overlay = bdrv_find_overlay(bs, base);
> BlockDriverState *above_base;
> + BlockDriverState *cor_filter_bs = NULL;
>
> if (!base_overlay) {
> error_setg(errp, "'%s' is not in the backing chain of '%s'",
> @@ -264,17 +268,36 @@ void stream_start(const char *job_id, BlockDriverState *bs,
> }
> }
>
> - /* Prevent concurrent jobs trying to modify the graph structure here, we
> - * already have our own plans. Also don't allow resize as the image size is
> - * queried only at the job start and then cached. */
> - s = block_job_create(job_id, &stream_job_driver, NULL, bs,
> - basic_flags | BLK_PERM_GRAPH_MOD,
> - basic_flags | BLK_PERM_WRITE,
> + cor_filter_bs = bdrv_cor_filter_append(bs, filter_node_name, errp);
> + if (cor_filter_bs == NULL) {
> + goto fail;
> + }
> +
> + if (bdrv_freeze_backing_chain(cor_filter_bs, bs, errp) < 0) {
> + bdrv_cor_filter_drop(cor_filter_bs);
> + cor_filter_bs = NULL;
> + goto fail;
> + }
> +
> + s = block_job_create(job_id, &stream_job_driver, NULL, cor_filter_bs,
> + BLK_PERM_CONSISTENT_READ,
> + basic_flags | BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD,
> speed, creation_flags, NULL, NULL, errp);
> if (!s) {
> goto fail;
> }
>
> + /*
> + * Prevent concurrent jobs trying to modify the graph structure here, we
> + * already have our own plans. Also don't allow resize as the image size is
> + * queried only at the job start and then cached.
> + */
> + if (block_job_add_bdrv(&s->common, "active node", bs,
> + basic_flags | BLK_PERM_GRAPH_MOD,
> + basic_flags | BLK_PERM_WRITE, &error_abort)) {
> + goto fail;
> + }
> +
> /* Block all intermediate nodes between bs and base, because they will
> * disappear from the chain after this operation. The streaming job reads
> * every block only once, assuming that it doesn't change, so forbid writes
> @@ -294,6 +317,8 @@ void stream_start(const char *job_id, BlockDriverState *bs,
>
> s->base_overlay = base_overlay;
> s->above_base = above_base;
> + s->cor_filter_bs = cor_filter_bs;
> + s->target_bs = bs;
> s->backing_file_str = g_strdup(backing_file_str);
> s->bs_read_only = bs_read_only;
> s->chain_frozen = true;
> @@ -307,5 +332,10 @@ fail:
> if (bs_read_only) {
> bdrv_reopen_set_read_only(bs, true, NULL);
> }
> - bdrv_unfreeze_backing_chain(bs, above_base);
> + if (cor_filter_bs) {
> + bdrv_unfreeze_backing_chain(cor_filter_bs, above_base);
> + bdrv_cor_filter_drop(cor_filter_bs);
> + } else {
> + bdrv_unfreeze_backing_chain(bs, above_base);
as I see, in this case chain is not yet frozen
> + }
> }
> diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
> index 1cdd7e2..fec9d89 100755
> --- a/tests/qemu-iotests/030
> +++ b/tests/qemu-iotests/030
> @@ -221,60 +221,20 @@ class TestParallelOps(iotests.QMPTestCase):
> for img in self.imgs:
> os.remove(img)
>
> - # Test that it's possible to run several block-stream operations
> - # in parallel in the same snapshot chain
> - def test_stream_parallel(self):
> - self.assert_no_active_block_jobs()
> -
> - # Check that the maps don't match before the streaming operations
> - for i in range(2, self.num_imgs, 2):
> - self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]),
> - qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]),
> - 'image file map matches backing file before streaming')
> -
> - # Create all streaming jobs
> - pending_jobs = []
> - for i in range(2, self.num_imgs, 2):
> - node_name = 'node%d' % i
> - job_id = 'stream-%s' % node_name
> - pending_jobs.append(job_id)
> - result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=512*1024)
> - self.assert_qmp(result, 'return', {})
> -
> - for job in pending_jobs:
> - result = self.vm.qmp('block-job-set-speed', device=job, speed=0)
> - self.assert_qmp(result, 'return', {})
> -
> - # Wait for all jobs to be finished.
> - while len(pending_jobs) > 0:
> - for event in self.vm.get_qmp_events(wait=True):
> - if event['event'] == 'BLOCK_JOB_COMPLETED':
> - job_id = self.dictpath(event, 'data/device')
> - self.assertTrue(job_id in pending_jobs)
> - self.assert_qmp_absent(event, 'data/error')
> - pending_jobs.remove(job_id)
> -
> - self.assert_no_active_block_jobs()
> - self.vm.shutdown()
> -
> - # Check that all maps match now
> - for i in range(2, self.num_imgs, 2):
> - self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
> - qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
> - 'image file map does not match backing file after streaming')
> -
> # Test that it's not possible to perform two block-stream
> # operations if there are nodes involved in both.
> def test_overlapping_1(self):
> self.assert_no_active_block_jobs()
>
> # Set a speed limit to make sure that this job blocks the rest
> - result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4', base=self.imgs[1], speed=1024*1024)
> + result = self.vm.qmp('block-stream', device='node4',
> + job_id='stream-node4', base=self.imgs[1],
> + filter_node_name='stream-filter', speed=1024*1024)
> self.assert_qmp(result, 'return', {})
>
> result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2])
> self.assert_qmp(result, 'error/desc',
> - "Node 'node4' is busy: block device is in use by block job: stream")
> + "Node 'stream-filter' is busy: block device is in use by block job: stream")
>
> result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3', base=self.imgs[2])
> self.assert_qmp(result, 'error/desc',
> @@ -287,7 +247,7 @@ class TestParallelOps(iotests.QMPTestCase):
> # block-commit should also fail if it touches nodes used by the stream job
> result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[4], job_id='commit-node4')
> self.assert_qmp(result, 'error/desc',
> - "Node 'node4' is busy: block device is in use by block job: stream")
> + "Node 'stream-filter' is busy: block device is in use by block job: stream")
>
> result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[1], top=self.imgs[3], job_id='commit-node1')
> self.assert_qmp(result, 'error/desc',
> diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out
> index 6d9bee1..5eb508d 100644
> --- a/tests/qemu-iotests/030.out
> +++ b/tests/qemu-iotests/030.out
> @@ -1,5 +1,5 @@
> -...........................
> +..........................
> ----------------------------------------------------------------------
> -Ran 27 tests
> +Ran 26 tests
>
> OK
> diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
> index 5035763..6c262a0 100755
> --- a/tests/qemu-iotests/245
> +++ b/tests/qemu-iotests/245
> @@ -898,17 +898,26 @@ class TestBlockdevReopen(iotests.QMPTestCase):
> # make hd1 read-only and block-stream requires it to be read-write
> # (Which error message appears depends on whether the stream job is
> # already done with copying at this point.)
> - self.reopen(opts, {},
> + # As the COR-filter node is inserted into the backing chain with the
> + # 'block-stream' operation, we move the options to their proper nodes.
> + opts = hd_opts(1)
> + opts['backing'] = hd_opts(2)
> + opts['backing']['backing'] = None
> + self.reopen(opts, {'read-only': True},
> ["Can't set node 'hd1' to r/o with copy-on-read enabled",
> "Cannot make block node read-only, there is a writer on it"])
>
> # We can't remove hd2 while the stream job is ongoing
> - opts['backing']['backing'] = None
> - self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
> + opts['backing'] = None
> + self.reopen(opts, {'read-only': False},
> + "Cannot change 'backing' link from 'hd1' to 'hd2'")
>
> - # We can detach hd1 from hd0 because it doesn't affect the stream job
> + # We can't detach hd1 from hd0 because there is the COR-filter implicit
> + # node in between.
> + opts = hd_opts(0)
> opts['backing'] = None
> - self.reopen(opts)
> + self.reopen(opts, {},
> + "Cannot change backing link if 'hd0' has an implicit backing file")
>
> self.vm.run_job('stream0', auto_finalize = False, auto_dismiss = True)
>
>
--
Best regards,
Vladimir
next prev parent reply other threads:[~2020-08-24 11:30 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-08-24 8:31 [PATCH v7 0/4] Apply COR-filter to the block-stream permanently Andrey Shinkevich
2020-08-24 8:31 ` [PATCH v7 1/4] copy-on-read: Support preadv/pwritev_part functions Andrey Shinkevich
2020-08-24 8:31 ` [PATCH v7 2/4] copy-on-read: add filter append/drop functions Andrey Shinkevich
2020-08-24 8:31 ` [PATCH v7 3/4] qapi: add filter-node-name to block-stream Andrey Shinkevich
2020-08-25 6:37 ` Markus Armbruster
2020-08-25 10:59 ` Andrey Shinkevich
2020-08-24 8:31 ` [PATCH v7 4/4] block: apply COR-filter to block-stream jobs Andrey Shinkevich
2020-08-24 11:30 ` Vladimir Sementsov-Ogievskiy [this message]
2020-08-28 16:28 ` Andrey Shinkevich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2eef369e-a79d-57c9-f8c8-40be9c3aaa2c@virtuozzo.com \
--to=vsementsov@virtuozzo.com \
--cc=andrey.shinkevich@virtuozzo.com \
--cc=armbru@redhat.com \
--cc=den@openvz.org \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=mreitz@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).