diff mbox

[v2,06/21] block: Exclude nested options only for children in append_open_options()

Message ID 1448294400-476-7-git-send-email-kwolf@redhat.com
State New
Headers show

Commit Message

Kevin Wolf Nov. 23, 2015, 3:59 p.m. UTC
Some drivers have nested options (e.g. blkdebug rule arrays), which
don't belong to a child node and shouldn't be removed. Don't remove all
options with "." in their name, but check for the complete prefixes of
actually existing child nodes.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 19 +++++++++++++++----
 include/block/block_int.h |  1 +
 2 files changed, 16 insertions(+), 4 deletions(-)

Comments

Wen Congyang Nov. 24, 2015, 1:03 a.m. UTC | #1
On 11/23/2015 11:59 PM, Kevin Wolf wrote:
> Some drivers have nested options (e.g. blkdebug rule arrays), which
> don't belong to a child node and shouldn't be removed. Don't remove all
> options with "." in their name, but check for the complete prefixes of
> actually existing child nodes.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                   | 19 +++++++++++++++----
>  include/block/block_int.h |  1 +
>  2 files changed, 16 insertions(+), 4 deletions(-)
> 
> diff --git a/block.c b/block.c
> index 23d9e10..02125e2 100644
> --- a/block.c
> +++ b/block.c
> @@ -1101,11 +1101,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
>  
>  static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>                                      BlockDriverState *child_bs,
> +                                    const char *child_name,
>                                      const BdrvChildRole *child_role)
>  {
>      BdrvChild *child = g_new(BdrvChild, 1);
>      *child = (BdrvChild) {
>          .bs     = child_bs,
> +        .name   = child_name,

The child_name may be allocated in the caller's stack. For example:
In the function quorum_open():
    for (i = 0; i < s->num_children; i++) {
        char indexstr[32];
        ret = snprintf(indexstr, 32, "children.%d", i);
        assert(ret < 32);

        s->children[i] = bdrv_open_child(NULL, options, indexstr, bs,
                                         &child_format, false, &local_err);
        if (local_err) {
            ret = -EINVAL;
            goto close_exit;
        }

        opened[i] = true;
    }

Thanks
Wen Congyang

>          .role   = child_role,
>      };
>  
> @@ -1165,7 +1167,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
>          bs->backing = NULL;
>          goto out;
>      }
> -    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
> +    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
>      bs->open_flags &= ~BDRV_O_NO_BACKING;
>      pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
>      pstrcpy(bs->backing_format, sizeof(bs->backing_format),
> @@ -1322,7 +1324,7 @@ BdrvChild *bdrv_open_child(const char *filename,
>          goto done;
>      }
>  
> -    c = bdrv_attach_child(parent, bs, child_role);
> +    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
>  
>  done:
>      qdict_del(options, bdref_key);
> @@ -3952,13 +3954,22 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
>  {
>      const QDictEntry *entry;
>      QemuOptDesc *desc;
> +    BdrvChild *child;
>      bool found_any = false;
> +    const char *p;
>  
>      for (entry = qdict_first(bs->options); entry;
>           entry = qdict_next(bs->options, entry))
>      {
> -        /* Only take options for this level */
> -        if (strchr(qdict_entry_key(entry), '.')) {
> +        /* Exclude options for children */
> +        QLIST_FOREACH(child, &bs->children, next) {
> +            if (strstart(qdict_entry_key(entry), child->name, &p)
> +                && (!*p || *p == '.'))
> +            {
> +                break;
> +            }
> +        }
> +        if (child) {
>              continue;
>          }
>  
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index 77dc165..b2325aa 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -351,6 +351,7 @@ extern const BdrvChildRole child_format;
>  
>  struct BdrvChild {
>      BlockDriverState *bs;
> +    const char *name;
>      const BdrvChildRole *role;
>      QLIST_ENTRY(BdrvChild) next;
>      QLIST_ENTRY(BdrvChild) next_parent;
>
Max Reitz Nov. 27, 2015, 5:58 p.m. UTC | #2
On 23.11.2015 16:59, Kevin Wolf wrote:
> Some drivers have nested options (e.g. blkdebug rule arrays), which
> don't belong to a child node and shouldn't be removed. Don't remove all
> options with "." in their name, but check for the complete prefixes of
> actually existing child nodes.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                   | 19 +++++++++++++++----
>  include/block/block_int.h |  1 +
>  2 files changed, 16 insertions(+), 4 deletions(-)

Thanks, now I don't need to fix it myself. :-)

(I would have had to do that for an in-work series of mine)

> diff --git a/block.c b/block.c
> index 23d9e10..02125e2 100644
> --- a/block.c
> +++ b/block.c
> @@ -1101,11 +1101,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
>  
>  static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>                                      BlockDriverState *child_bs,
> +                                    const char *child_name,
>                                      const BdrvChildRole *child_role)
>  {
>      BdrvChild *child = g_new(BdrvChild, 1);
>      *child = (BdrvChild) {
>          .bs     = child_bs,
> +        .name   = child_name,
>          .role   = child_role,
>      };
>  
> @@ -1165,7 +1167,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
>          bs->backing = NULL;
>          goto out;
>      }
> -    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
> +    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
>      bs->open_flags &= ~BDRV_O_NO_BACKING;
>      pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
>      pstrcpy(bs->backing_format, sizeof(bs->backing_format),
> @@ -1322,7 +1324,7 @@ BdrvChild *bdrv_open_child(const char *filename,
>          goto done;
>      }
>  
> -    c = bdrv_attach_child(parent, bs, child_role);
> +    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
>  
>  done:
>      qdict_del(options, bdref_key);
> @@ -3952,13 +3954,22 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
>  {
>      const QDictEntry *entry;
>      QemuOptDesc *desc;
> +    BdrvChild *child;
>      bool found_any = false;
> +    const char *p;
>  
>      for (entry = qdict_first(bs->options); entry;
>           entry = qdict_next(bs->options, entry))
>      {
> -        /* Only take options for this level */
> -        if (strchr(qdict_entry_key(entry), '.')) {
> +        /* Exclude options for children */
> +        QLIST_FOREACH(child, &bs->children, next) {
> +            if (strstart(qdict_entry_key(entry), child->name, &p)
> +                && (!*p || *p == '.'))
> +            {
> +                break;
> +            }
> +        }
> +        if (child) {
>              continue;
>          }
>  

A good general solution, but I think bdrv_refresh_filename() may be bad
enough to break with general solutions. ;-)

bdrv_refresh_filename() only considers "file" and "backing" (actually,
it only supports "file" for now, I'm working on "backing", though). The
only drivers with other children are quorum, blkdebug, blkverify and
VMDK. The former three have their own implementation of
bdrv_refresh_filename(), so they don't use append_open_options() at all.
The latter, however, (VMDK) does not.

This change to append_open_options results in the extent.%d options
simply being omitted altogether because bdrv_refresh_filename() does not
fetch them. Before, they were included in the VMDK BDS's options, which
is not ideal but works more or less.

In order to "fix" this, I see three ways right now:
1. Just care about "file" and "backing". bdrv_refresh_filename()
   doesn't support anything else, so that will be fine.
2. Implement bdrv_refresh_filename() specifically for VMDK so
   append_open_options() will never have to handle anything but "file"
   and "backing".
3. Fix bdrv_refresh_filename() so that it handles all children and not
   just "file" and "backing".

Since we are shooting for 2.6 anyway (I assume ;-)), I think we should
go for option 3. This means that this patch is fine, and I'll see to
fixing bdrv_refresh_filename() (because I'm working on that anyway).

Reviewed-by: Max Reitz <mreitz@redhat.com>
Max Reitz Nov. 27, 2015, 6:02 p.m. UTC | #3
On 27.11.2015 18:58, Max Reitz wrote:
> On 23.11.2015 16:59, Kevin Wolf wrote:
>> Some drivers have nested options (e.g. blkdebug rule arrays), which
>> don't belong to a child node and shouldn't be removed. Don't remove all
>> options with "." in their name, but check for the complete prefixes of
>> actually existing child nodes.
>>
>> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>> ---
>>  block.c                   | 19 +++++++++++++++----
>>  include/block/block_int.h |  1 +
>>  2 files changed, 16 insertions(+), 4 deletions(-)
> 
> Thanks, now I don't need to fix it myself. :-)
> 
> (I would have had to do that for an in-work series of mine)
> 
>> diff --git a/block.c b/block.c
>> index 23d9e10..02125e2 100644
>> --- a/block.c
>> +++ b/block.c
>> @@ -1101,11 +1101,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
>>  
>>  static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>>                                      BlockDriverState *child_bs,
>> +                                    const char *child_name,
>>                                      const BdrvChildRole *child_role)
>>  {
>>      BdrvChild *child = g_new(BdrvChild, 1);
>>      *child = (BdrvChild) {
>>          .bs     = child_bs,
>> +        .name   = child_name,
>>          .role   = child_role,
>>      };
>>  
>> @@ -1165,7 +1167,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
>>          bs->backing = NULL;
>>          goto out;
>>      }
>> -    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
>> +    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
>>      bs->open_flags &= ~BDRV_O_NO_BACKING;
>>      pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
>>      pstrcpy(bs->backing_format, sizeof(bs->backing_format),
>> @@ -1322,7 +1324,7 @@ BdrvChild *bdrv_open_child(const char *filename,
>>          goto done;
>>      }
>>  
>> -    c = bdrv_attach_child(parent, bs, child_role);
>> +    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
>>  
>>  done:
>>      qdict_del(options, bdref_key);
>> @@ -3952,13 +3954,22 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
>>  {
>>      const QDictEntry *entry;
>>      QemuOptDesc *desc;
>> +    BdrvChild *child;
>>      bool found_any = false;
>> +    const char *p;
>>  
>>      for (entry = qdict_first(bs->options); entry;
>>           entry = qdict_next(bs->options, entry))
>>      {
>> -        /* Only take options for this level */
>> -        if (strchr(qdict_entry_key(entry), '.')) {
>> +        /* Exclude options for children */
>> +        QLIST_FOREACH(child, &bs->children, next) {
>> +            if (strstart(qdict_entry_key(entry), child->name, &p)
>> +                && (!*p || *p == '.'))
>> +            {
>> +                break;
>> +            }
>> +        }
>> +        if (child) {
>>              continue;
>>          }
>>  
> 
> A good general solution, but I think bdrv_refresh_filename() may be bad
> enough to break with general solutions. ;-)
> 
> bdrv_refresh_filename() only considers "file" and "backing" (actually,
> it only supports "file" for now, I'm working on "backing", though). The
> only drivers with other children are quorum, blkdebug, blkverify and
> VMDK. The former three have their own implementation of
> bdrv_refresh_filename(), so they don't use append_open_options() at all.
> The latter, however, (VMDK) does not.
> 
> This change to append_open_options results in the extent.%d options
> simply being omitted altogether because bdrv_refresh_filename() does not
> fetch them. Before, they were included in the VMDK BDS's options, which
> is not ideal but works more or less.
> 
> In order to "fix" this, I see three ways right now:
> 1. Just care about "file" and "backing". bdrv_refresh_filename()
>    doesn't support anything else, so that will be fine.
> 2. Implement bdrv_refresh_filename() specifically for VMDK so
>    append_open_options() will never have to handle anything but "file"
>    and "backing".
> 3. Fix bdrv_refresh_filename() so that it handles all children and not
>    just "file" and "backing".
> 
> Since we are shooting for 2.6 anyway (I assume ;-)), I think we should
> go for option 3. This means that this patch is fine, and I'll see to
> fixing bdrv_refresh_filename() (because I'm working on that anyway).
> 
> Reviewed-by: Max Reitz <mreitz@redhat.com>

Oops, focused so much on this that I missed the issue Wen Congyang
found. So this R-b is assuming that is fixed *cough*.

Max
Kevin Wolf Nov. 30, 2015, 9:01 a.m. UTC | #4
Am 27.11.2015 um 18:58 hat Max Reitz geschrieben:
> On 23.11.2015 16:59, Kevin Wolf wrote:
> > Some drivers have nested options (e.g. blkdebug rule arrays), which
> > don't belong to a child node and shouldn't be removed. Don't remove all
> > options with "." in their name, but check for the complete prefixes of
> > actually existing child nodes.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  block.c                   | 19 +++++++++++++++----
> >  include/block/block_int.h |  1 +
> >  2 files changed, 16 insertions(+), 4 deletions(-)
> 
> Thanks, now I don't need to fix it myself. :-)
> 
> (I would have had to do that for an in-work series of mine)
> 
> > diff --git a/block.c b/block.c
> > index 23d9e10..02125e2 100644
> > --- a/block.c
> > +++ b/block.c
> > @@ -1101,11 +1101,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
> >  
> >  static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
> >                                      BlockDriverState *child_bs,
> > +                                    const char *child_name,
> >                                      const BdrvChildRole *child_role)
> >  {
> >      BdrvChild *child = g_new(BdrvChild, 1);
> >      *child = (BdrvChild) {
> >          .bs     = child_bs,
> > +        .name   = child_name,
> >          .role   = child_role,
> >      };
> >  
> > @@ -1165,7 +1167,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
> >          bs->backing = NULL;
> >          goto out;
> >      }
> > -    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
> > +    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
> >      bs->open_flags &= ~BDRV_O_NO_BACKING;
> >      pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
> >      pstrcpy(bs->backing_format, sizeof(bs->backing_format),
> > @@ -1322,7 +1324,7 @@ BdrvChild *bdrv_open_child(const char *filename,
> >          goto done;
> >      }
> >  
> > -    c = bdrv_attach_child(parent, bs, child_role);
> > +    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
> >  
> >  done:
> >      qdict_del(options, bdref_key);
> > @@ -3952,13 +3954,22 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
> >  {
> >      const QDictEntry *entry;
> >      QemuOptDesc *desc;
> > +    BdrvChild *child;
> >      bool found_any = false;
> > +    const char *p;
> >  
> >      for (entry = qdict_first(bs->options); entry;
> >           entry = qdict_next(bs->options, entry))
> >      {
> > -        /* Only take options for this level */
> > -        if (strchr(qdict_entry_key(entry), '.')) {
> > +        /* Exclude options for children */
> > +        QLIST_FOREACH(child, &bs->children, next) {
> > +            if (strstart(qdict_entry_key(entry), child->name, &p)
> > +                && (!*p || *p == '.'))
> > +            {
> > +                break;
> > +            }
> > +        }
> > +        if (child) {
> >              continue;
> >          }
> >  
> 
> A good general solution, but I think bdrv_refresh_filename() may be bad
> enough to break with general solutions. ;-)
> 
> bdrv_refresh_filename() only considers "file" and "backing" (actually,
> it only supports "file" for now, I'm working on "backing", though). The
> only drivers with other children are quorum, blkdebug, blkverify and
> VMDK. The former three have their own implementation of
> bdrv_refresh_filename(), so they don't use append_open_options() at all.
> The latter, however, (VMDK) does not.
> 
> This change to append_open_options results in the extent.%d options
> simply being omitted altogether because bdrv_refresh_filename() does not
> fetch them. Before, they were included in the VMDK BDS's options, which
> is not ideal but works more or less.

Are you sure? As far as I can tell, this patch should only keep options
that were previously removed, but not remove options that were
previously kept (with the exception of direct use of child names, which
I added here to address your review comments for v1).

Specifically for "extents.%d", this is a child name and is therefore
omitted. However, it contains a '.', so it was already removed without
this patch.

I'm accepting proof of the contrary in the form of a test case. ;-)

> In order to "fix" this, I see three ways right now:
> 1. Just care about "file" and "backing". bdrv_refresh_filename()
>    doesn't support anything else, so that will be fine.
> 2. Implement bdrv_refresh_filename() specifically for VMDK so
>    append_open_options() will never have to handle anything but "file"
>    and "backing".
> 3. Fix bdrv_refresh_filename() so that it handles all children and not
>    just "file" and "backing".
> 
> Since we are shooting for 2.6 anyway (I assume ;-)), I think we should
> go for option 3. This means that this patch is fine, and I'll see to
> fixing bdrv_refresh_filename() (because I'm working on that anyway).

Yes, I agree.

Kevin
Max Reitz Nov. 30, 2015, 4:13 p.m. UTC | #5
On 30.11.2015 10:01, Kevin Wolf wrote:
> Am 27.11.2015 um 18:58 hat Max Reitz geschrieben:
>> On 23.11.2015 16:59, Kevin Wolf wrote:
>>> Some drivers have nested options (e.g. blkdebug rule arrays), which
>>> don't belong to a child node and shouldn't be removed. Don't remove all
>>> options with "." in their name, but check for the complete prefixes of
>>> actually existing child nodes.
>>>
>>> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>>> ---
>>>  block.c                   | 19 +++++++++++++++----
>>>  include/block/block_int.h |  1 +
>>>  2 files changed, 16 insertions(+), 4 deletions(-)
>>
>> Thanks, now I don't need to fix it myself. :-)
>>
>> (I would have had to do that for an in-work series of mine)
>>
>>> diff --git a/block.c b/block.c
>>> index 23d9e10..02125e2 100644
>>> --- a/block.c
>>> +++ b/block.c
>>> @@ -1101,11 +1101,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
>>>  
>>>  static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>>>                                      BlockDriverState *child_bs,
>>> +                                    const char *child_name,
>>>                                      const BdrvChildRole *child_role)
>>>  {
>>>      BdrvChild *child = g_new(BdrvChild, 1);
>>>      *child = (BdrvChild) {
>>>          .bs     = child_bs,
>>> +        .name   = child_name,
>>>          .role   = child_role,
>>>      };
>>>  
>>> @@ -1165,7 +1167,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
>>>          bs->backing = NULL;
>>>          goto out;
>>>      }
>>> -    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
>>> +    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
>>>      bs->open_flags &= ~BDRV_O_NO_BACKING;
>>>      pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
>>>      pstrcpy(bs->backing_format, sizeof(bs->backing_format),
>>> @@ -1322,7 +1324,7 @@ BdrvChild *bdrv_open_child(const char *filename,
>>>          goto done;
>>>      }
>>>  
>>> -    c = bdrv_attach_child(parent, bs, child_role);
>>> +    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
>>>  
>>>  done:
>>>      qdict_del(options, bdref_key);
>>> @@ -3952,13 +3954,22 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
>>>  {
>>>      const QDictEntry *entry;
>>>      QemuOptDesc *desc;
>>> +    BdrvChild *child;
>>>      bool found_any = false;
>>> +    const char *p;
>>>  
>>>      for (entry = qdict_first(bs->options); entry;
>>>           entry = qdict_next(bs->options, entry))
>>>      {
>>> -        /* Only take options for this level */
>>> -        if (strchr(qdict_entry_key(entry), '.')) {
>>> +        /* Exclude options for children */
>>> +        QLIST_FOREACH(child, &bs->children, next) {
>>> +            if (strstart(qdict_entry_key(entry), child->name, &p)
>>> +                && (!*p || *p == '.'))
>>> +            {
>>> +                break;
>>> +            }
>>> +        }
>>> +        if (child) {
>>>              continue;
>>>          }
>>>  
>>
>> A good general solution, but I think bdrv_refresh_filename() may be bad
>> enough to break with general solutions. ;-)
>>
>> bdrv_refresh_filename() only considers "file" and "backing" (actually,
>> it only supports "file" for now, I'm working on "backing", though). The
>> only drivers with other children are quorum, blkdebug, blkverify and
>> VMDK. The former three have their own implementation of
>> bdrv_refresh_filename(), so they don't use append_open_options() at all.
>> The latter, however, (VMDK) does not.
>>
>> This change to append_open_options results in the extent.%d options
>> simply being omitted altogether because bdrv_refresh_filename() does not
>> fetch them. Before, they were included in the VMDK BDS's options, which
>> is not ideal but works more or less.
> 
> Are you sure? As far as I can tell, this patch should only keep options
> that were previously removed, but not remove options that were
> previously kept (with the exception of direct use of child names, which
> I added here to address your review comments for v1).
> 
> Specifically for "extents.%d", this is a child name and is therefore
> omitted. However, it contains a '.', so it was already removed without
> this patch.

Right, it is broken already. The same applies to qcow2's options
containing a dot.

Max

> I'm accepting proof of the contrary in the form of a test case. ;-)
> 
>> In order to "fix" this, I see three ways right now:
>> 1. Just care about "file" and "backing". bdrv_refresh_filename()
>>    doesn't support anything else, so that will be fine.
>> 2. Implement bdrv_refresh_filename() specifically for VMDK so
>>    append_open_options() will never have to handle anything but "file"
>>    and "backing".
>> 3. Fix bdrv_refresh_filename() so that it handles all children and not
>>    just "file" and "backing".
>>
>> Since we are shooting for 2.6 anyway (I assume ;-)), I think we should
>> go for option 3. This means that this patch is fine, and I'll see to
>> fixing bdrv_refresh_filename() (because I'm working on that anyway).
> 
> Yes, I agree.
> 
> Kevin
>
diff mbox

Patch

diff --git a/block.c b/block.c
index 23d9e10..02125e2 100644
--- a/block.c
+++ b/block.c
@@ -1101,11 +1101,13 @@  static int bdrv_fill_options(QDict **options, const char **pfilename,
 
 static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
                                     BlockDriverState *child_bs,
+                                    const char *child_name,
                                     const BdrvChildRole *child_role)
 {
     BdrvChild *child = g_new(BdrvChild, 1);
     *child = (BdrvChild) {
         .bs     = child_bs,
+        .name   = child_name,
         .role   = child_role,
     };
 
@@ -1165,7 +1167,7 @@  void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
         bs->backing = NULL;
         goto out;
     }
-    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
+    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
     bs->open_flags &= ~BDRV_O_NO_BACKING;
     pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
     pstrcpy(bs->backing_format, sizeof(bs->backing_format),
@@ -1322,7 +1324,7 @@  BdrvChild *bdrv_open_child(const char *filename,
         goto done;
     }
 
-    c = bdrv_attach_child(parent, bs, child_role);
+    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
 
 done:
     qdict_del(options, bdref_key);
@@ -3952,13 +3954,22 @@  static bool append_open_options(QDict *d, BlockDriverState *bs)
 {
     const QDictEntry *entry;
     QemuOptDesc *desc;
+    BdrvChild *child;
     bool found_any = false;
+    const char *p;
 
     for (entry = qdict_first(bs->options); entry;
          entry = qdict_next(bs->options, entry))
     {
-        /* Only take options for this level */
-        if (strchr(qdict_entry_key(entry), '.')) {
+        /* Exclude options for children */
+        QLIST_FOREACH(child, &bs->children, next) {
+            if (strstart(qdict_entry_key(entry), child->name, &p)
+                && (!*p || *p == '.'))
+            {
+                break;
+            }
+        }
+        if (child) {
             continue;
         }
 
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 77dc165..b2325aa 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -351,6 +351,7 @@  extern const BdrvChildRole child_format;
 
 struct BdrvChild {
     BlockDriverState *bs;
+    const char *name;
     const BdrvChildRole *role;
     QLIST_ENTRY(BdrvChild) next;
     QLIST_ENTRY(BdrvChild) next_parent;