Re: [PATCH v7 39/47] blockdev: Fix active commit choice

2020-08-24 Thread Kevin Wolf
Am 24.08.2020 um 16:41 hat Max Reitz geschrieben:
> On 24.08.20 16:07, Kevin Wolf wrote:
> > Am 24.08.2020 um 15:18 hat Max Reitz geschrieben:
> >> On 21.08.20 17:50, Kevin Wolf wrote:
> >>> Am 25.06.2020 um 17:22 hat Max Reitz geschrieben:
>  We have to perform an active commit whenever the top node has a parent
>  that has taken the WRITE permission on it.
> 
>  Signed-off-by: Max Reitz 
>  Reviewed-by: Vladimir Sementsov-Ogievskiy 
>  ---
>   blockdev.c | 24 +---
>   1 file changed, 21 insertions(+), 3 deletions(-)
> 
>  diff --git a/blockdev.c b/blockdev.c
>  index 402f1d1df1..237fffbe53 100644
>  --- a/blockdev.c
>  +++ b/blockdev.c
>  @@ -2589,6 +2589,7 @@ void qmp_block_commit(bool has_job_id, const char 
>  *job_id, const char *device,
>   AioContext *aio_context;
>   Error *local_err = NULL;
>   int job_flags = JOB_DEFAULT;
>  +uint64_t top_perm, top_shared;
>   
>   if (!has_speed) {
>   speed = 0;
>  @@ -2704,14 +2705,31 @@ void qmp_block_commit(bool has_job_id, const 
>  char *job_id, const char *device,
>   goto out;
>   }
>   
>  -if (top_bs == bs) {
>  +/*
>  + * Active commit is required if and only if someone has taken a
>  + * WRITE permission on the top node.
> >>>
> >>> ...or if someone wants to take a WRITE permission while the job is
> >>> running.
> >>>
> >>> Future intentions of the user is something that we can't know, so maybe
> >>> this should become an option in the future (not in this series, of
> >>> course).
> >>>
> Historically, we have always
>  + * used active commit for top nodes, so continue that practice.
>  + * (Active commit is never really wrong.)
>  + */
> >>>
> >>> Changing the practice would break compatibility with clients that start
> >>> an active commit job and then attach it to a read-write device, so we
> >>> must continue the practice. I think the comment should be clearer about
> >>> this, it sounds more like "no reason, but why not".
> >>
> >> I think that’s what I meant by “historically”.  Is “legacily” a word?
> >>
> >> But sure, I can make it more explicit.
> >>
> >>> This is even more problematic because the commit job doesn't unshare
> >>> BLK_PERM_WRITE yet, so it would lead to silent corruption rather than an
> >>> error.
> >>>
>  +bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
>  +if (top_perm & BLK_PERM_WRITE ||
>  +bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
>  +{
>   if (has_backing_file) {
>   error_setg(errp, "'backing-file' specified,"
>    " but 'top' is the active layer");
> >>>
> >>> Hm, this error message isn't accurate any more.
> >>>
> >>> In fact, the implementation isn't consistent with the QAPI documentation
> >>> any more, because backing-file is only an error for the top level.
> >>
> >> Hm.  I wanted to agree, and then I wanted to come up with a QAPI
> >> documentation that fits the new behavior (because I think it makes more
> >> sense to change the QAPI documentation along with the behavior change,
> >> rather than to force us to allow backing-file for anything that isn’t on
> >> the top layer).
> >>
> >> But in the process of coming up with a better description, I noticed
> >> that this doesn’t say “is a root node”, it says “is the active layer”.
> >> I would say a node in the active layer is a node that has some parent
> >> that has taken a WRITE permission on it.  So actually I think that the
> >> documentation is right, and this code only now fits.
> > 
> > Then you may have not only "the" active layer, but multiple active
> > layers. I find this a bit counterintuitive.
> 
> Depends on what you count as a layer.  I don’t think that’s a clearly
> defined term, is it?  I only know of “active layer”, “format layer”,
> “protocol layer”, and you can at least have multiple format layers above
> each other.  So I don’t find it counterintuitive.
> 
> But perhaps it’d be best to just get away from the term “active layer”,
> as you propose below.

Hm, if I needed to describe what a layer is for me intuitively, I guess
it would be something like each non-filter node on a node chain with all
of the filters directly on top of it?

Depending on which link you follow, you get different sets of layers:
For bs->file, you get the format/protocol layer distinction. For
bs->backing, you get essentially what bdrv_backing_chain_next()
iterates.

In this context (which is talking about COW overlays), I expected the
bs->backing link to apply.

The active layer is then the COW layer that is directly referenced by a
guest device, block job or block export.

> > There is a simple reason why backing-file is an error for a root node:
> > It doesn't have overlays, so a valu

Re: [PATCH v7 39/47] blockdev: Fix active commit choice

2020-08-24 Thread Max Reitz
On 24.08.20 16:07, Kevin Wolf wrote:
> Am 24.08.2020 um 15:18 hat Max Reitz geschrieben:
>> On 21.08.20 17:50, Kevin Wolf wrote:
>>> Am 25.06.2020 um 17:22 hat Max Reitz geschrieben:
 We have to perform an active commit whenever the top node has a parent
 that has taken the WRITE permission on it.

 Signed-off-by: Max Reitz 
 Reviewed-by: Vladimir Sementsov-Ogievskiy 
 ---
  blockdev.c | 24 +---
  1 file changed, 21 insertions(+), 3 deletions(-)

 diff --git a/blockdev.c b/blockdev.c
 index 402f1d1df1..237fffbe53 100644
 --- a/blockdev.c
 +++ b/blockdev.c
 @@ -2589,6 +2589,7 @@ void qmp_block_commit(bool has_job_id, const char 
 *job_id, const char *device,
  AioContext *aio_context;
  Error *local_err = NULL;
  int job_flags = JOB_DEFAULT;
 +uint64_t top_perm, top_shared;
  
  if (!has_speed) {
  speed = 0;
 @@ -2704,14 +2705,31 @@ void qmp_block_commit(bool has_job_id, const char 
 *job_id, const char *device,
  goto out;
  }
  
 -if (top_bs == bs) {
 +/*
 + * Active commit is required if and only if someone has taken a
 + * WRITE permission on the top node.
>>>
>>> ...or if someone wants to take a WRITE permission while the job is
>>> running.
>>>
>>> Future intentions of the user is something that we can't know, so maybe
>>> this should become an option in the future (not in this series, of
>>> course).
>>>
Historically, we have always
 + * used active commit for top nodes, so continue that practice.
 + * (Active commit is never really wrong.)
 + */
>>>
>>> Changing the practice would break compatibility with clients that start
>>> an active commit job and then attach it to a read-write device, so we
>>> must continue the practice. I think the comment should be clearer about
>>> this, it sounds more like "no reason, but why not".
>>
>> I think that’s what I meant by “historically”.  Is “legacily” a word?
>>
>> But sure, I can make it more explicit.
>>
>>> This is even more problematic because the commit job doesn't unshare
>>> BLK_PERM_WRITE yet, so it would lead to silent corruption rather than an
>>> error.
>>>
 +bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
 +if (top_perm & BLK_PERM_WRITE ||
 +bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
 +{
  if (has_backing_file) {
  error_setg(errp, "'backing-file' specified,"
   " but 'top' is the active layer");
>>>
>>> Hm, this error message isn't accurate any more.
>>>
>>> In fact, the implementation isn't consistent with the QAPI documentation
>>> any more, because backing-file is only an error for the top level.
>>
>> Hm.  I wanted to agree, and then I wanted to come up with a QAPI
>> documentation that fits the new behavior (because I think it makes more
>> sense to change the QAPI documentation along with the behavior change,
>> rather than to force us to allow backing-file for anything that isn’t on
>> the top layer).
>>
>> But in the process of coming up with a better description, I noticed
>> that this doesn’t say “is a root node”, it says “is the active layer”.
>> I would say a node in the active layer is a node that has some parent
>> that has taken a WRITE permission on it.  So actually I think that the
>> documentation is right, and this code only now fits.
> 
> Then you may have not only "the" active layer, but multiple active
> layers. I find this a bit counterintuitive.

Depends on what you count as a layer.  I don’t think that’s a clearly
defined term, is it?  I only know of “active layer”, “format layer”,
“protocol layer”, and you can at least have multiple format layers above
each other.  So I don’t find it counterintuitive.

But perhaps it’d be best to just get away from the term “active layer”,
as you propose below.

> There is a simple reason why backing-file is an error for a root node:
> It doesn't have overlays, so a value to write to the header of overlay
> images just doesn't make sense.

Ah, yeah...

> The same reasoning doesn't apply for writable images that do have
> overlays. Forbidding backing-file is a more arbitrary restriction there.
> I'm not saying that we can't make arbitrary restrictions where allowing
> an option is not worth the effort, but I feel they should be spelt out
> more explicitly instead of twisting words like "active layer" until they
> fit the code.

I’m all for spelling it out more explicitly.  I just noticed that I
couldn’t clearly distinguish “active layer” from “other” cases of nodes
with writers on them, which is why I noted that “active” to me means the
post-patch behavior already.

You’re right that there is no semantic reason for making it an error.
So I just want it to be an error to be lazy.  I hope you let me do that.

Re: [PATCH v7 39/47] blockdev: Fix active commit choice

2020-08-24 Thread Kevin Wolf
Am 24.08.2020 um 15:18 hat Max Reitz geschrieben:
> On 21.08.20 17:50, Kevin Wolf wrote:
> > Am 25.06.2020 um 17:22 hat Max Reitz geschrieben:
> >> We have to perform an active commit whenever the top node has a parent
> >> that has taken the WRITE permission on it.
> >>
> >> Signed-off-by: Max Reitz 
> >> Reviewed-by: Vladimir Sementsov-Ogievskiy 
> >> ---
> >>  blockdev.c | 24 +---
> >>  1 file changed, 21 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/blockdev.c b/blockdev.c
> >> index 402f1d1df1..237fffbe53 100644
> >> --- a/blockdev.c
> >> +++ b/blockdev.c
> >> @@ -2589,6 +2589,7 @@ void qmp_block_commit(bool has_job_id, const char 
> >> *job_id, const char *device,
> >>  AioContext *aio_context;
> >>  Error *local_err = NULL;
> >>  int job_flags = JOB_DEFAULT;
> >> +uint64_t top_perm, top_shared;
> >>  
> >>  if (!has_speed) {
> >>  speed = 0;
> >> @@ -2704,14 +2705,31 @@ void qmp_block_commit(bool has_job_id, const char 
> >> *job_id, const char *device,
> >>  goto out;
> >>  }
> >>  
> >> -if (top_bs == bs) {
> >> +/*
> >> + * Active commit is required if and only if someone has taken a
> >> + * WRITE permission on the top node.
> > 
> > ...or if someone wants to take a WRITE permission while the job is
> > running.
> > 
> > Future intentions of the user is something that we can't know, so maybe
> > this should become an option in the future (not in this series, of
> > course).
> > 
> >>Historically, we have always
> >> + * used active commit for top nodes, so continue that practice.
> >> + * (Active commit is never really wrong.)
> >> + */
> > 
> > Changing the practice would break compatibility with clients that start
> > an active commit job and then attach it to a read-write device, so we
> > must continue the practice. I think the comment should be clearer about
> > this, it sounds more like "no reason, but why not".
> 
> I think that’s what I meant by “historically”.  Is “legacily” a word?
> 
> But sure, I can make it more explicit.
> 
> > This is even more problematic because the commit job doesn't unshare
> > BLK_PERM_WRITE yet, so it would lead to silent corruption rather than an
> > error.
> > 
> >> +bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
> >> +if (top_perm & BLK_PERM_WRITE ||
> >> +bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
> >> +{
> >>  if (has_backing_file) {
> >>  error_setg(errp, "'backing-file' specified,"
> >>   " but 'top' is the active layer");
> > 
> > Hm, this error message isn't accurate any more.
> > 
> > In fact, the implementation isn't consistent with the QAPI documentation
> > any more, because backing-file is only an error for the top level.
> 
> Hm.  I wanted to agree, and then I wanted to come up with a QAPI
> documentation that fits the new behavior (because I think it makes more
> sense to change the QAPI documentation along with the behavior change,
> rather than to force us to allow backing-file for anything that isn’t on
> the top layer).
> 
> But in the process of coming up with a better description, I noticed
> that this doesn’t say “is a root node”, it says “is the active layer”.
> I would say a node in the active layer is a node that has some parent
> that has taken a WRITE permission on it.  So actually I think that the
> documentation is right, and this code only now fits.

Then you may have not only "the" active layer, but multiple active
layers. I find this a bit counterintuitive.

There is a simple reason why backing-file is an error for a root node:
It doesn't have overlays, so a value to write to the header of overlay
images just doesn't make sense.

The same reasoning doesn't apply for writable images that do have
overlays. Forbidding backing-file is a more arbitrary restriction there.
I'm not saying that we can't make arbitrary restrictions where allowing
an option is not worth the effort, but I feel they should be spelt out
more explicitly instead of twisting words like "active layer" until they
fit the code.

> Though I do think this wants for some clarification.  Perhaps “If 'top'
> is the active layer (i.e., is a node that may be written to), specifying
> a backing [...]”?

"If 'top' doesn't have an overlay image or is in use by a writer..."?

> There’s more wrong with the specification, namely the whole part under
> @backing-file past the “(Since 2.1)”, starting with “If top == base”.  I
> think all of that should go to the top level.  (And “If top == active”
> should be changed to “If top is active (i.e., may be written to)”.)

At least the latter only becomes wrong with this patch, so I think it
needs to be changed by this patch.

Kevin


signature.asc
Description: PGP signature


Re: [PATCH v7 39/47] blockdev: Fix active commit choice

2020-08-24 Thread Max Reitz
On 21.08.20 17:50, Kevin Wolf wrote:
> Am 25.06.2020 um 17:22 hat Max Reitz geschrieben:
>> We have to perform an active commit whenever the top node has a parent
>> that has taken the WRITE permission on it.
>>
>> Signed-off-by: Max Reitz 
>> Reviewed-by: Vladimir Sementsov-Ogievskiy 
>> ---
>>  blockdev.c | 24 +---
>>  1 file changed, 21 insertions(+), 3 deletions(-)
>>
>> diff --git a/blockdev.c b/blockdev.c
>> index 402f1d1df1..237fffbe53 100644
>> --- a/blockdev.c
>> +++ b/blockdev.c
>> @@ -2589,6 +2589,7 @@ void qmp_block_commit(bool has_job_id, const char 
>> *job_id, const char *device,
>>  AioContext *aio_context;
>>  Error *local_err = NULL;
>>  int job_flags = JOB_DEFAULT;
>> +uint64_t top_perm, top_shared;
>>  
>>  if (!has_speed) {
>>  speed = 0;
>> @@ -2704,14 +2705,31 @@ void qmp_block_commit(bool has_job_id, const char 
>> *job_id, const char *device,
>>  goto out;
>>  }
>>  
>> -if (top_bs == bs) {
>> +/*
>> + * Active commit is required if and only if someone has taken a
>> + * WRITE permission on the top node.
> 
> ...or if someone wants to take a WRITE permission while the job is
> running.
> 
> Future intentions of the user is something that we can't know, so maybe
> this should become an option in the future (not in this series, of
> course).
> 
>>Historically, we have always
>> + * used active commit for top nodes, so continue that practice.
>> + * (Active commit is never really wrong.)
>> + */
> 
> Changing the practice would break compatibility with clients that start
> an active commit job and then attach it to a read-write device, so we
> must continue the practice. I think the comment should be clearer about
> this, it sounds more like "no reason, but why not".

I think that’s what I meant by “historically”.  Is “legacily” a word?

But sure, I can make it more explicit.

> This is even more problematic because the commit job doesn't unshare
> BLK_PERM_WRITE yet, so it would lead to silent corruption rather than an
> error.
> 
>> +bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
>> +if (top_perm & BLK_PERM_WRITE ||
>> +bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
>> +{
>>  if (has_backing_file) {
>>  error_setg(errp, "'backing-file' specified,"
>>   " but 'top' is the active layer");
> 
> Hm, this error message isn't accurate any more.
> 
> In fact, the implementation isn't consistent with the QAPI documentation
> any more, because backing-file is only an error for the top level.

Hm.  I wanted to agree, and then I wanted to come up with a QAPI
documentation that fits the new behavior (because I think it makes more
sense to change the QAPI documentation along with the behavior change,
rather than to force us to allow backing-file for anything that isn’t on
the top layer).

But in the process of coming up with a better description, I noticed
that this doesn’t say “is a root node”, it says “is the active layer”.
I would say a node in the active layer is a node that has some parent
that has taken a WRITE permission on it.  So actually I think that the
documentation is right, and this code only now fits.

Though I do think this wants for some clarification.  Perhaps “If 'top'
is the active layer (i.e., is a node that may be written to), specifying
a backing [...]”?

There’s more wrong with the specification, namely the whole part under
@backing-file past the “(Since 2.1)”, starting with “If top == base”.  I
think all of that should go to the top level.  (And “If top == active”
should be changed to “If top is active (i.e., may be written to)”.)

Max



signature.asc
Description: OpenPGP digital signature


Re: [PATCH v7 39/47] blockdev: Fix active commit choice

2020-08-21 Thread Kevin Wolf
Am 25.06.2020 um 17:22 hat Max Reitz geschrieben:
> We have to perform an active commit whenever the top node has a parent
> that has taken the WRITE permission on it.
> 
> Signed-off-by: Max Reitz 
> Reviewed-by: Vladimir Sementsov-Ogievskiy 
> ---
>  blockdev.c | 24 +---
>  1 file changed, 21 insertions(+), 3 deletions(-)
> 
> diff --git a/blockdev.c b/blockdev.c
> index 402f1d1df1..237fffbe53 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -2589,6 +2589,7 @@ void qmp_block_commit(bool has_job_id, const char 
> *job_id, const char *device,
>  AioContext *aio_context;
>  Error *local_err = NULL;
>  int job_flags = JOB_DEFAULT;
> +uint64_t top_perm, top_shared;
>  
>  if (!has_speed) {
>  speed = 0;
> @@ -2704,14 +2705,31 @@ void qmp_block_commit(bool has_job_id, const char 
> *job_id, const char *device,
>  goto out;
>  }
>  
> -if (top_bs == bs) {
> +/*
> + * Active commit is required if and only if someone has taken a
> + * WRITE permission on the top node.

...or if someone wants to take a WRITE permission while the job is
running.

Future intentions of the user is something that we can't know, so maybe
this should become an option in the future (not in this series, of
course).

>Historically, we have always
> + * used active commit for top nodes, so continue that practice.
> + * (Active commit is never really wrong.)
> + */

Changing the practice would break compatibility with clients that start
an active commit job and then attach it to a read-write device, so we
must continue the practice. I think the comment should be clearer about
this, it sounds more like "no reason, but why not".

This is even more problematic because the commit job doesn't unshare
BLK_PERM_WRITE yet, so it would lead to silent corruption rather than an
error.

> +bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
> +if (top_perm & BLK_PERM_WRITE ||
> +bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
> +{
>  if (has_backing_file) {
>  error_setg(errp, "'backing-file' specified,"
>   " but 'top' is the active layer");

Hm, this error message isn't accurate any more.

In fact, the implementation isn't consistent with the QAPI documentation
any more, because backing-file is only an error for the top level.

>  goto out;
>  }
> -commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
> -job_flags, speed, on_error,
> +if (!has_job_id) {
> +/*
> + * Emulate here what block_job_create() does, because it
> + * is possible that @bs != @top_bs (the block job should
> + * be named after @bs, even if @top_bs is the actual
> + * source)
> + */

Should it? Oh, yes, looks like it. block-commit is weird. :-)

> +job_id = bdrv_get_device_name(bs);
> +}
> +commit_active_start(job_id, top_bs, base_bs, job_flags, speed, 
> on_error,
>  filter_node_name, NULL, NULL, false, &local_err);
>  } else {
>  BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);

Kevin




[PATCH v7 39/47] blockdev: Fix active commit choice

2020-06-25 Thread Max Reitz
We have to perform an active commit whenever the top node has a parent
that has taken the WRITE permission on it.

Signed-off-by: Max Reitz 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
---
 blockdev.c | 24 +---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 402f1d1df1..237fffbe53 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2589,6 +2589,7 @@ void qmp_block_commit(bool has_job_id, const char 
*job_id, const char *device,
 AioContext *aio_context;
 Error *local_err = NULL;
 int job_flags = JOB_DEFAULT;
+uint64_t top_perm, top_shared;
 
 if (!has_speed) {
 speed = 0;
@@ -2704,14 +2705,31 @@ void qmp_block_commit(bool has_job_id, const char 
*job_id, const char *device,
 goto out;
 }
 
-if (top_bs == bs) {
+/*
+ * Active commit is required if and only if someone has taken a
+ * WRITE permission on the top node.  Historically, we have always
+ * used active commit for top nodes, so continue that practice.
+ * (Active commit is never really wrong.)
+ */
+bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
+if (top_perm & BLK_PERM_WRITE ||
+bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
+{
 if (has_backing_file) {
 error_setg(errp, "'backing-file' specified,"
  " but 'top' is the active layer");
 goto out;
 }
-commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
-job_flags, speed, on_error,
+if (!has_job_id) {
+/*
+ * Emulate here what block_job_create() does, because it
+ * is possible that @bs != @top_bs (the block job should
+ * be named after @bs, even if @top_bs is the actual
+ * source)
+ */
+job_id = bdrv_get_device_name(bs);
+}
+commit_active_start(job_id, top_bs, base_bs, job_flags, speed, 
on_error,
 filter_node_name, NULL, NULL, false, &local_err);
 } else {
 BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
-- 
2.26.2