qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v1 12/16] qapi: fix example of blockdev-add command


From: Victor Toso
Subject: Re: [PATCH v1 12/16] qapi: fix example of blockdev-add command
Date: Fri, 2 Sep 2022 10:02:55 +0200

Hi,

On Thu, Sep 01, 2022 at 01:13:19PM +0200, Markus Armbruster wrote:
> Victor Toso <victortoso@redhat.com> writes:
> 
> > Hi,
> >
> > First of all, I'm happy that this patch got us into this
> > discussion.
> 
> Me too!
> 
> > On Wed, Aug 31, 2022 at 04:53:49PM +0200, Markus Armbruster wrote:
> >> Victor Toso <victortoso@redhat.com> writes:
> >>
> >> > Hi,
> >> >
> >> > On Wed, Aug 31, 2022 at 03:16:54PM +0200, Markus Armbruster wrote:
> >> >> Cc: Kevin for an improved chance of getting any nonsense I might write
> >> >> corrected.
> >> >>
> >> >> Victor Toso <victortoso@redhat.com> writes:
> >> >>
> >> >> > Hi,
> >> >> >
> >> >> > On Wed, Aug 31, 2022 at 01:40:50PM +0200, Markus Armbruster wrote:
> >> >> >> Victor Toso <victortoso@redhat.com> writes:
> >> >> >>
> >> >> >> > The example output is setting optional member "backing" with null.
> >> >> >> > This has no runtime impact. Remove it.
> >> >> >> >
> >> >> >> > Problem was noticed when using the example as a test case for Go
> >> >> >> > bindings.
> >> >> >>
> >> >> >> "Fix example" and "problem" implies there's something wrong.
> >> >> >> "No runtime impact" sounds like it's redundant, but not wrong.
> >> >> >> Wrong or not wrong?
> >> >> >
> >> >> > I take your comment is more about the wording which is confusing.
> >> >> >
> >> >> > Would it be better if I change to:
> >> >> > '''
> >> >> >    The example output is setting optional member "backing" with
> >> >> >    null. While this has no runtime impact, setting optional
> >> >> >    members with empty value should not be encouraged. Remove it.
> >> >> > '''
> >> >> >
> >> >> > While I think the above is true, my main reason for proposing
> >> >> > this change is to re-use the example as a test case, but I'm not
> >> >> > sure if adding anything related to it would make it better (only
> >> >> > more confusing!).
> >> >>
> >> >> I had a closer look at the schema.
> >> >>
> >> >> The definition of backing is
> >> >>
> >> >>     ##
> >> >>     # @BlockdevOptionsGenericCOWFormat:
> >> >>     #
> >> >>     # Driver specific block device options for image format that have 
> >> >> no option
> >> >>     # besides their data source and an optional backing file.
> >> >>     #
> >> >>     # @backing: reference to or definition of the backing file block
> >> >>     #           device, null disables the backing file entirely.
> >> >>     #           Defaults to the backing file stored the image file.
> >> >>     #
> >> >>     # Since: 2.9
> >> >>     ##
> >> >>     { 'struct': 'BlockdevOptionsGenericCOWFormat',
> >> >>       'base': 'BlockdevOptionsGenericFormat',
> >> >>       'data': { '*backing': 'BlockdevRefOrNull' } }
> >> >>
> >> >> Meaning, if I remember correctly (with some help from commit
> >> >> c42e8742f52's message):
> >> >>
> >> >> 1. Present @backing
> >> >>
> >> >> 1.a. of type 'str' means use the existing block device with this ID as
> >> >>      backing image
> >> >>
> >> >> 1.b. of type 'BlockdevOptions' means use the new block device defined by
> >> >>      it as backing image
> >> >>
> >> >> 1.c. that is null means use no backing image
> >> >>
> >> >> 2. Absent @backing means default to the backing file named in the COW
> >> >>    image.
> >> >
> >> > Over the wire, how you get the difference between 1.c and 2? Are
> >> > you saying that for optional member "backing" we should be
> >> > explicit sending null over the wire?
> >>
> >> In the QAPI schema language, absent optional members do not default to
> >> any specific value.  Or in other words, "absent" is distinct from
> >> "present with value V" for any value V.
> >>
> >> Now, the *semantics* of "absent" are often identical to some default
> >> value.  Documentation should then say something like (default:
> >> DEFAULT-VALUE).
> >
> > Yep, this is fine.
> >
> >> In this particular instance, it isn't: "absent" means something else
> >> than any possible value.
> >
> > The major painpoint for me is that, in Go an optional member is a
> > field with a pointer to that field's type. A pointer is default
> > initialized with nil and if the user of the Go module does
> > nothing with it, we naturally omit it in the output JSON.
> >
> > This needs to be workaround in two cases so far:
> > BlockdevRefOrNull and StrOrNull. This two alternate types are the
> > only ones that take JSON null as value. I'm sure I'll make it
> > work.
> 
> Losely related:
> 
> commit 481b002cc81ed7fc7b06e32e9d4d495d81739d14
> Author: Markus Armbruster <armbru@redhat.com>
> Date:   Wed Apr 29 15:35:05 2015 -0600
> 
>     qobject: Add a special null QObject
>     
>     I'm going to fix the JSON parser to recognize null.  The obvious
>     representation of JSON null as (QObject *)NULL doesn't work, because
>     the parser already uses it as an error value.  Perhaps we should
>     change it to free NULL for null, but that's more than I can do right
>     now.  Create a special null QObject instead.
>     
>     The existing QDict, QList, and QString all represent something that
>     is a pointer in C and could therefore be associated with NULL.  But
>     right now, all three of these sub-types are always non-null once
>     created, so the new null sentinel object is intentionally unrelated
>     to them.
>     
>     Signed-off-by: Markus Armbruster <armbru@redhat.com>
>     Signed-off-by: Eric Blake <eblake@redhat.com>
>     Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
> 
> I figure something like this should work for Go as well, i.e. create a
> singleton type to represent the QAPI type 'null'.
> 
> > --
> >
> > Now, should we really keep using null type as alternative way of
> > expressing "disabling feature" or even "use something else"?
> >
> > I'd be happy to work on improving this if that's reasonable. My
> > 2c bellow.
> >
> >     ##
> >     # @BlockdevRefOrNull:
> >     #
> >     # Reference to a block device.
> >     #
> >     # @definition: defines a new block device inline
> >     # @reference: references the ID of an existing block device.
> >     #             An empty string means that no block device should
> >     #             be referenced.  Deprecated; use null instead.
> >     # @null: No block device should be referenced (since 2.10)
> >     #
> >     # Since: 2.9
> >     ##
> >     { 'alternate': 'BlockdevRefOrNull',
> >       'data': { 'definition': 'BlockdevOptions',
> >                 'reference': 'str',
> >                 'null': 'null' } }
> >
> > BlockdevRefOrNull is only used by BlockdevOptionsGenericCOWFormat
> > which is used by BlockdevOptions ('qed' and 'vmdk') and extend by
> > BlockdevOptionsQcow and BlockdevOptionsQcow2.
> >
> > As you pointed out before, setting backing to null means
> > disabling. This is expressed in both BlockdevRefOrNull and
> > @BlockdevOptionsGenericCOWFormat documentation.
> >
> > IMHO the idea of disabling the default is fine but would be
> > better expressed with a boolean type, something like:
> >
> >     { 'alternate': 'BlockdevRefOrNull',
> >       'data': { 'definition': 'BlockdevOptions',
> >                 'reference': 'str',
> >                 'enable': 'bool' } }
> >
> > Which makes { backing: false } a bit nicer to my newbie eyes. If
> > backing member is made non optional, { backing: true } should
> > have the same value as omitting an optional backing member.
> 
> So:
> 
> 1. Present @backing
> 
> 1.a. of type 'str' means use the existing block device with this ID as
>      backing image
> 
> 1.b. of type 'BlockdevOptions' means use the new block device defined by
>      it as backing image
> 
> 1.c. that is false means use no backing image
> 
> 1.d. that is true means use the backing file named in the COW
>      image.
> 
> 2. Absent @backing defaults to true, i.e. 1.d.
> 
> Looks fine to me as an interface, if I ignore compatibility concerns.
> 
> We could quibble about the name 'enable'.  It's not part of the external
> interface.
> 
> Sadly, the idea runs afoul a QAPI restriction:
> 
>     scripts/qapi-gen.py: In file included from ../qapi/qapi-schema.json:68:
>     In file included from ../qapi/block.json:8:
>     ../qapi/block-core.json: In alternate 'BlockdevRefOrNull':
>     ../qapi/block-core.json:4362: branch 'enabled' can't be distinguished 
> from 'reference'
> 
> This is due to
> 
> commit c0644771ebedbd8f47f3c24816445e30111d226b (tag: pull-qapi-2017-05-31)
> Author: Markus Armbruster <armbru@redhat.com>
> Date:   Mon May 22 18:42:15 2017 +0200
> 
>     qapi: Reject alternates that can't work with keyval_parse()
>     
>     Alternates are sum types like unions, but use the JSON type on the
>     wire / QType in QObject instead of an explicit tag.  That's why we
>     require alternate members to have distinct QTypes.
>     
>     The recently introduced keyval_parse() (commit d454dbe) can only
>     produce string scalars.  The qobject_input_visitor_new_keyval() input
>     visitor mostly hides the difference, so code using a QObject input
>     visitor doesn't have to care whether its input was parsed from JSON or
>     KEY=VALUE,...  The difference leaks for alternates, as noted in commit
>     0ee9ae7: a non-string, non-enum scalar alternate value can't currently
>     be expressed.
>     
>     In part, this is just our insufficiently sophisticated implementation.
>     Consider alternate type 'GuestFileWhence'.  It has an integer member
>     and a 'QGASeek' member.  The latter is an enumeration with values
>     'set', 'cur', 'end'.  The meaning of b=set, b=cur, b=end, b=0, b=1 and
>     so forth is perfectly obvious.  However, our current implementation
>     falls apart at run time for b=0, b=1, and so forth.  Fixable, but not
>     today; add a test case and a TODO comment.
>     
>     Now consider an alternate type with a string and an integer member.
>     What's the meaning of a=42?  Is it the string "42" or the integer 42?
>     Whichever meaning you pick makes the other inexpressible.  This isn't
>     just an implementation problem, it's fundamental.  Our current
>     implementation will pick string.
>     
>     So far, we haven't needed such alternates.  To make sure we stop and
>     think before we add one that cannot sanely work with keyval_parse(),
>     let's require alternate members to have sufficiently distinct
>     representation in KEY=VALUE,... syntax:
>     
>     * A string member clashes with any other scalar member
>     
>     * An enumeration member clashes with bool members when it has value
>       'on' or 'off'.
>     
>     * An enumeration member clashes with numeric members when it has a
>       value that starts with '-', '+', or a decimal digit.  This is a
>       rather lazy approximation of the actual number syntax accepted by
>       the visitor.
>     
>       Note that enumeration values starting with '-' and '+' are rejected
>       elsewhere already, but better safe than sorry.
>     
>     Signed-off-by: Markus Armbruster <armbru@redhat.com>
>     Message-Id: <1495471335-23707-5-git-send-email-armbru@redhat.com>
>     Reviewed-by: Eric Blake <eblake@redhat.com>
>     Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Astute readers may now wonder why our string member does *not* clash
> with scalar member null.  The answer is kind of embarrassing: because
> the keyval input visitor supports numbers and booleans, but not null.
> If you need null, you get to use JSON.
> 
> If we enhanced the visitor to support null like it supports booleans,
> the value null would become ambiguous, and to remove this ambiguity,
> we'd have to make 'str' clash with 'null'.
> 
> I've come to regret adding 'null' to the QAPI schema language.  I didn't
> fully understand how this innocent-looking feature was going to interact
> with everything else.  More trouble than it's worth.
> 
> The dotted keys syntax as an alternative to JSON is also plenty of
> trouble, but we knew that, and accepted it because there's also worth.
> 
> >     ##
> >     # @StrOrNull:
> >     #
> >     # This is a string value or the explicit lack of a string (null
> >     # pointer in C).  Intended for cases when 'optional absent' already
> >     # has a different meaning.
> >     #
> >     # @s: the string value
> >     # @n: no string value
> >     #
> >     # Since: 2.10
> >     ##
> >     { 'alternate': 'StrOrNull',
> >       'data': { 's': 'str',
> >                 'n': 'null' } }
> >
> > StrOrNull is used in MigrateSetParameters (*tls-creds,
> > *tls-hostname, *tls-authz):
> >   JSON null: disable specifics or the entirety of migrating
> >              with tls.
> >   "" (empty string): Uses some specifics default.
> >   omitted: Likely to error if using x509 tls?
> >
> >   Similarly, a boolean would make more sense to express disabled?
> >
> >     
> > https://gitlab.com/qemu-project/qemu/-/commit/4af245dc3e6e5c96405b3edb9d75657504256469?view=parallel
> >
> > StrOrNull is also used in x-blockdev-set-iothread in the iothread
> > member, documented as follow:
> >
> >    # @iothread: the name of the IOThread object or null for the
> >    # main loop
> >
> > iothread here is non optional, meaning that the user has to set a
> > string with the name of IOThread object or null. This really
> > seems a case where iothread could be optional "str" instead of
> > StrOrNull where omitting defaults to main loop.
> 
> Then "absent" is not the same as "present with value V" for any value V.
> That's okay; it conforms to the schema language design.  I just don't
> like that part of the schema language design.  To be clear: insufficient
> reason to reject a patch.
> 
> We'd have to provide backward compatibility somehow.
> 
> >> Aside: no, I don't like this part of the QAPI schema language design
> >> either.  "Absent defaults to DEFAULT-VALUE" is easier to explain and
> >> understand.
> >
> > Well, there always room for improving.
> 
> Here's a concrete improvement I'd like to see some day: extend the QAPI
> language to support optionals with a default value, i.e. absent is
> indistinguishable from present with the default value.  Then use this
> for as many optionals as we can.

This would be nice indeed.  I've filed an issue in Gitlab to
track it:

    https://gitlab.com/qemu-project/qemu/-/issues/1188

Cheers,
Victor

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]