The code already don't freeze base node and we try to make it prepared
for the situation when base node is changed during the operation. In
other words, block-stream doesn't own base node.
Let's introduce a new interface which should replace the current one,
which will in better relations with the code. Specifying bottom node
instead of base, and requiring it to be non-filter gives us the
following benefits:
- drop difference between above_base and base_overlay, which will be
renamed to just bottom, when old interface dropped
- clean way to work with parallel streams/commits on the same backing
chain, which otherwise become a problem when we introduce a filter
for stream job
- cleaner interface. Nobody will surprised the fact that base node may
disappear during block-stream, when there is no word about "base" in
the interface.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
qapi/block-core.json | 12 ++++---
include/block/block_int.h | 1 +
block/monitor/block-hmp-cmds.c | 3 +-
block/stream.c | 50 +++++++++++++++++++---------
blockdev.c | 59 ++++++++++++++++++++++++++++------
5 files changed, 94 insertions(+), 31 deletions(-)
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b8094a5ec7..cb0066fd5c 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2517,10 +2517,14 @@
# @device: the device or node name of the top image
#
# @base: the common backing file name.
-# It cannot be set if @base-node is also set.
+# It cannot be set if @base-node or @bottom is also set.
#
# @base-node: the node name of the backing file.
-# It cannot be set if @base is also set. (Since 2.8)
+# It cannot be set if @base or @bottom is also set. (Since 2.8)
+#
+# @bottom: the last node in the chain that should be streamed into
+# top. It cannot be set if @base or @base-node is also set.
+# It cannot be filter node. (Since 6.0)
#
# @backing-file: The backing file string to write into the top
# image. This filename is not validated.
@@ -2576,8 +2580,8 @@
##
{ 'command': 'block-stream',
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
- '*base-node': 'str', '*backing-file': 'str', '*speed': 'int',
- '*on-error': 'BlockdevOnError',
+ '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str',
+ '*speed': 'int', '*on-error': 'BlockdevOnError',
'*filter-node-name': 'str',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 1f56443440..4b8aa61fb4 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -1160,6 +1160,7 @@ int is_windows_drive(const char *filename);
*/
void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, const char *backing_file_str,
+ BlockDriverState *bottom,
int creation_flags, int64_t speed,
BlockdevOnError on_error,
const char *filter_node_name,
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index e8a58f326e..afd75ab628 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -507,7 +507,8 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
qmp_block_stream(true, device, device, base != NULL, base, false, NULL,
- false, NULL, qdict_haskey(qdict, "speed"), speed, true,
+ false, NULL, false, NULL,
+ qdict_haskey(qdict, "speed"), speed, true,
BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false,
false, &error);
diff --git a/block/stream.c b/block/stream.c
index 6a525a5edf..045d6bc76b 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -221,6 +221,7 @@ static const BlockJobDriver stream_job_driver = {
void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, const char *backing_file_str,
+ BlockDriverState *bottom,
int creation_flags, int64_t speed,
BlockdevOnError on_error,
const char *filter_node_name,
@@ -230,25 +231,42 @@ void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *iter;
bool bs_read_only;
int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
- BlockDriverState *base_overlay = bdrv_find_overlay(bs, base);
+ BlockDriverState *base_overlay;
BlockDriverState *above_base;
- if (!base_overlay) {
- error_setg(errp, "'%s' is not in the backing chain of '%s'",
- base->node_name, bs->node_name);
- return;
- }
+ assert(!(base && bottom));
+ assert(!(backing_file_str && bottom));
+
+ if (bottom) {
+ /*
+ * New simple interface. The code is written in terms of old interface
+ * with @base parameter (still, it doesn't freeze link to base, so in
+ * this mean old code is correct for new interface). So, for now, just
+ * emulate base_overlay and above_base. Still, when old interface
+ * finally removed, we should refactor code to use only "bottom", but
+ * not "*base*" things.
+ */
+ assert(!bottom->drv->is_filter);
+ base_overlay = above_base = bottom;
+ } else {
+ base_overlay = bdrv_find_overlay(bs, base);
+ if (!base_overlay) {
+ error_setg(errp, "'%s' is not in the backing chain of '%s'",
+ base->node_name, bs->node_name);
+ return;
+ }
- /*
- * Find the node directly above @base. @base_overlay is a COW overlay, so
- * it must have a bdrv_cow_child(), but it is the immediate overlay of
- * @base, so between the two there can only be filters.
- */
- above_base = base_overlay;
- if (bdrv_cow_bs(above_base) != base) {
- above_base = bdrv_cow_bs(above_base);
- while (bdrv_filter_bs(above_base) != base) {
- above_base = bdrv_filter_bs(above_base);
+ /*
+ * Find the node directly above @base. @base_overlay is a COW overlay,
+ * so it must have a bdrv_cow_child(), but it is the immediate overlay
+ * of @base, so between the two there can only be filters.
+ */
+ above_base = base_overlay;
+ if (bdrv_cow_bs(above_base) != base) {
+ above_base = bdrv_cow_bs(above_base);
+ while (bdrv_filter_bs(above_base) != base) {
+ above_base = bdrv_filter_bs(above_base);
+ }
}
}
diff --git a/blockdev.c b/blockdev.c
index b58f36fc31..18699d3cda 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2499,6 +2499,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bool has_base, const char *base,
bool has_base_node, const char *base_node,
bool has_backing_file, const char *backing_file,
+ bool has_bottom, const char *bottom,
bool has_speed, int64_t speed,
bool has_on_error, BlockdevOnError on_error,
bool has_filter_node_name, const char *filter_node_name,
@@ -2506,12 +2507,31 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
{
- BlockDriverState *bs, *iter;
+ BlockDriverState *bs, *iter, *iter_end;
BlockDriverState *base_bs = NULL;
+ BlockDriverState *bottom_bs = NULL;
AioContext *aio_context;
Error *local_err = NULL;
int job_flags = JOB_DEFAULT;
+ if (has_base && has_base_node) {
+ error_setg(errp, "'base' and 'base-node' cannot be specified "
+ "at the same time");
+ return;
+ }
+
+ if (has_base && has_bottom) {
+ error_setg(errp, "'base' and 'bottom' cannot be specified "
+ "at the same time");
+ return;
+ }
+
+ if (has_bottom && has_base_node) {
+ error_setg(errp, "'bottom' and 'base-node' cannot be specified "
+ "at the same time");
+ return;
+ }
+
if (!has_on_error) {
on_error = BLOCKDEV_ON_ERROR_REPORT;
}
@@ -2524,12 +2544,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (has_base && has_base_node) {
- error_setg(errp, "'base' and 'base-node' cannot be specified "
- "at the same time");
- goto out;
- }
-
if (has_base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
@@ -2553,8 +2567,33 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bdrv_refresh_filename(base_bs);
}
- /* Check for op blockers in the whole chain between bs and base */
- for (iter = bs; iter && iter != base_bs;
+ if (has_bottom) {
+ bottom_bs = bdrv_lookup_bs(NULL, bottom, errp);
+ if (!bottom_bs) {
+ goto out;
+ }
+ if (!bottom_bs->drv) {
+ error_setg(errp, "Node '%s' is not open", bottom);
+ goto out;
+ }
+ if (bottom_bs->drv->is_filter) {
+ error_setg(errp, "Node '%s' is a filter, use a non-filter node "
+ "as 'bottom'", bottom);
+ goto out;
+ }
+ if (!bdrv_chain_contains(bs, bottom_bs)) {
+ error_setg(errp, "Node '%s' is not in a chain starting from '%s'",
+ bottom, device);
+ goto out;
+ }
+ assert(bdrv_get_aio_context(bottom_bs) == aio_context);
+ }
+
+ /*
+ * Check for op blockers in the whole chain between bs and base (or bottom)
+ */
+ iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
+ for (iter = bs; iter && iter != iter_end;
iter = bdrv_filter_or_cow_bs(iter))
{
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
@@ -2578,7 +2617,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
}
stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
- job_flags, has_speed ? speed : 0, on_error,
+ bottom_bs, job_flags, has_speed ? speed : 0, on_error,
filter_node_name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
--
2.25.4
On 16.12.20 07:17, Vladimir Sementsov-Ogievskiy wrote: > The code already don't freeze base node and we try to make it prepared > for the situation when base node is changed during the operation. In > other words, block-stream doesn't own base node. > > Let's introduce a new interface which should replace the current one, > which will in better relations with the code. Specifying bottom node > instead of base, and requiring it to be non-filter gives us the > following benefits: > > - drop difference between above_base and base_overlay, which will be > renamed to just bottom, when old interface dropped > > - clean way to work with parallel streams/commits on the same backing > chain, which otherwise become a problem when we introduce a filter > for stream job > > - cleaner interface. Nobody will surprised the fact that base node may > disappear during block-stream, when there is no word about "base" in > the interface. > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> > --- > qapi/block-core.json | 12 ++++--- > include/block/block_int.h | 1 + > block/monitor/block-hmp-cmds.c | 3 +- > block/stream.c | 50 +++++++++++++++++++--------- > blockdev.c | 59 ++++++++++++++++++++++++++++------ > 5 files changed, 94 insertions(+), 31 deletions(-) > > diff --git a/qapi/block-core.json b/qapi/block-core.json > index b8094a5ec7..cb0066fd5c 100644 > --- a/qapi/block-core.json > +++ b/qapi/block-core.json > @@ -2517,10 +2517,14 @@ > # @device: the device or node name of the top image > # > # @base: the common backing file name. > -# It cannot be set if @base-node is also set. > +# It cannot be set if @base-node or @bottom is also set. > # > # @base-node: the node name of the backing file. > -# It cannot be set if @base is also set. (Since 2.8) > +# It cannot be set if @base or @bottom is also set. (Since 2.8) > +# > +# @bottom: the last node in the chain that should be streamed into > +# top. It cannot be set if @base or @base-node is also set. > +# It cannot be filter node. (Since 6.0) As far as I can make out, one of the results of our discussion on v14 was that when using backing-file + bottom, we want to require the user to specify backing-fmt as well. Now, backing-fmt isn’t present yet. Doesn’t that mean we have to make bottom + backing-file an error until we have backing-fmt (like it was in v14)? Or do you consider the change to patch 9 sufficient to make at least the case work for which backing-file was purportedly introduced, i.e. FD passing? (Patch 9’s new version will just take the format of the base post-streaming, which would be most likely a correct guess when using FD passing.) (I.e., now with patch 9 being more liberal in guessing, perhaps you decided to no longer make backing-fmt mandatory after all.) Max
22.12.2020 19:07, Max Reitz wrote: > On 16.12.20 07:17, Vladimir Sementsov-Ogievskiy wrote: >> The code already don't freeze base node and we try to make it prepared >> for the situation when base node is changed during the operation. In >> other words, block-stream doesn't own base node. >> >> Let's introduce a new interface which should replace the current one, >> which will in better relations with the code. Specifying bottom node >> instead of base, and requiring it to be non-filter gives us the >> following benefits: >> >> - drop difference between above_base and base_overlay, which will be >> renamed to just bottom, when old interface dropped >> >> - clean way to work with parallel streams/commits on the same backing >> chain, which otherwise become a problem when we introduce a filter >> for stream job >> >> - cleaner interface. Nobody will surprised the fact that base node may >> disappear during block-stream, when there is no word about "base" in >> the interface. >> >> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> >> --- >> qapi/block-core.json | 12 ++++--- >> include/block/block_int.h | 1 + >> block/monitor/block-hmp-cmds.c | 3 +- >> block/stream.c | 50 +++++++++++++++++++--------- >> blockdev.c | 59 ++++++++++++++++++++++++++++------ >> 5 files changed, 94 insertions(+), 31 deletions(-) >> >> diff --git a/qapi/block-core.json b/qapi/block-core.json >> index b8094a5ec7..cb0066fd5c 100644 >> --- a/qapi/block-core.json >> +++ b/qapi/block-core.json >> @@ -2517,10 +2517,14 @@ >> # @device: the device or node name of the top image >> # >> # @base: the common backing file name. >> -# It cannot be set if @base-node is also set. >> +# It cannot be set if @base-node or @bottom is also set. >> # >> # @base-node: the node name of the backing file. >> -# It cannot be set if @base is also set. (Since 2.8) >> +# It cannot be set if @base or @bottom is also set. (Since 2.8) >> +# >> +# @bottom: the last node in the chain that should be streamed into >> +# top. It cannot be set if @base or @base-node is also set. >> +# It cannot be filter node. (Since 6.0) > > As far as I can make out, one of the results of our discussion on v14 was that when using backing-file + bottom, we want to require the user to specify backing-fmt as well. Now, backing-fmt isn’t present yet. Doesn’t that mean we have to make bottom + backing-file an error until we have backing-fmt (like it was in v14)? See my answer on 09. I just have some doubts around backing-fmt and decided to keep it as is. I don't think that we really need backing-fmt. We shouldn't have use-cases when backing-fmt is set to something another than final base node. Therefore, using format_name of final base node is a correct thing to do. So, I don't see the reason now for introducing new option. > > Or do you consider the change to patch 9 sufficient to make at least the case work for which backing-file was purportedly introduced, i.e. FD passing? (Patch 9’s new version will just take the format of the base post-streaming, which would be most likely a correct guess when using FD passing.) Yes, I decided just to keep logic around backing-fmt as is. Hmm, definitely I should have described my decision in cover letter, for you not being surprised. > > (I.e., now with patch 9 being more liberal in guessing, perhaps you decided to no longer make backing-fmt mandatory after all.) > > Max > -- Best regards, Vladimir
On 22.12.20 19:00, Vladimir Sementsov-Ogievskiy wrote: > 22.12.2020 19:07, Max Reitz wrote: >> On 16.12.20 07:17, Vladimir Sementsov-Ogievskiy wrote: >>> The code already don't freeze base node and we try to make it prepared >>> for the situation when base node is changed during the operation. In >>> other words, block-stream doesn't own base node. >>> >>> Let's introduce a new interface which should replace the current one, >>> which will in better relations with the code. Specifying bottom node >>> instead of base, and requiring it to be non-filter gives us the >>> following benefits: >>> >>> - drop difference between above_base and base_overlay, which will be >>> renamed to just bottom, when old interface dropped >>> >>> - clean way to work with parallel streams/commits on the same backing >>> chain, which otherwise become a problem when we introduce a filter >>> for stream job >>> >>> - cleaner interface. Nobody will surprised the fact that base node may >>> disappear during block-stream, when there is no word about "base" in >>> the interface. >>> >>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> >>> --- >>> qapi/block-core.json | 12 ++++--- >>> include/block/block_int.h | 1 + >>> block/monitor/block-hmp-cmds.c | 3 +- >>> block/stream.c | 50 +++++++++++++++++++--------- >>> blockdev.c | 59 ++++++++++++++++++++++++++++------ >>> 5 files changed, 94 insertions(+), 31 deletions(-) >>> >>> diff --git a/qapi/block-core.json b/qapi/block-core.json >>> index b8094a5ec7..cb0066fd5c 100644 >>> --- a/qapi/block-core.json >>> +++ b/qapi/block-core.json >>> @@ -2517,10 +2517,14 @@ >>> # @device: the device or node name of the top image >>> # >>> # @base: the common backing file name. >>> -# It cannot be set if @base-node is also set. >>> +# It cannot be set if @base-node or @bottom is also set. >>> # >>> # @base-node: the node name of the backing file. >>> -# It cannot be set if @base is also set. (Since 2.8) >>> +# It cannot be set if @base or @bottom is also set. >>> (Since 2.8) >>> +# >>> +# @bottom: the last node in the chain that should be streamed into >>> +# top. It cannot be set if @base or @base-node is also set. >>> +# It cannot be filter node. (Since 6.0) >> >> As far as I can make out, one of the results of our discussion on v14 >> was that when using backing-file + bottom, we want to require the user >> to specify backing-fmt as well. Now, backing-fmt isn’t present yet. >> Doesn’t that mean we have to make bottom + backing-file an error until >> we have backing-fmt (like it was in v14)? > > See my answer on 09. I just have some doubts around backing-fmt and > decided to keep it as is. > > I don't think that we really need backing-fmt. We shouldn't have > use-cases when backing-fmt is set to something another than final base > node. Therefore, using format_name of final base node is a correct thing > to do. So, I don't see the reason now for introducing new option. Yup, yup, all good. Reviewed-by: Max Reitz <mreitz@redhat.com>
22.12.2020 21:00, Vladimir Sementsov-Ogievskiy wrote: > We shouldn't have use-cases when backing-fmt is set to something another than final base node. I mean, we shouldn't have use-cases when backing-file is [...] -- Best regards, Vladimir
© 2016 - 2026 Red Hat, Inc.