[PATCH v15 10/13] qapi: block-stream: add "bottom" argument

Vladimir Sementsov-Ogievskiy posted 13 patches 5 years, 1 month ago
[PATCH v15 10/13] qapi: block-stream: add "bottom" argument
Posted by Vladimir Sementsov-Ogievskiy 5 years, 1 month ago
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


Re: [PATCH v15 10/13] qapi: block-stream: add "bottom" argument
Posted by Max Reitz 5 years, 1 month ago
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


Re: [PATCH v15 10/13] qapi: block-stream: add "bottom" argument
Posted by Vladimir Sementsov-Ogievskiy 5 years, 1 month ago
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

Re: [PATCH v15 10/13] qapi: block-stream: add "bottom" argument
Posted by Max Reitz 5 years, 1 month ago
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>


Re: [PATCH v15 10/13] qapi: block-stream: add "bottom" argument
Posted by Vladimir Sementsov-Ogievskiy 5 years, 1 month ago
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