qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Pierrick Bouvier <pierrick.bouvier@linaro.org>
To: Markus Armbruster <armbru@redhat.com>
Cc: qemu-devel@nongnu.org, richard.henderson@linaro.org,
	stefanha@redhat.com, "Michael Roth" <michael.roth@amd.com>,
	pbonzini@redhat.com, berrange@redhat.com,
	peter.maydell@linaro.org, thuth@redhat.com, jsnow@redhat.com,
	philmd@linaro.org, "Alex Bennée" <alex.bennee@linaro.org>,
	devel@lists.libvirt.org, "Victor Toso" <victortoso@redhat.com>
Subject: Re: [RFC PATCH 0/3] single-binary: make QAPI generated files common
Date: Mon, 28 Apr 2025 09:05:19 -0700	[thread overview]
Message-ID: <25bb4527-f145-4d9c-8f91-a962bfa14a6f@linaro.org> (raw)
In-Reply-To: <87a583789z.fsf@pond.sub.org>

On 4/25/25 11:21 PM, Markus Armbruster wrote:
> Trouble is some uses of the second kind are in QAPI conditionals.  I can
> see three options:
> 
> (1) Drop these conditionals.
> 
> (2) Replace them by run-time checks.
> 
> (3) Have target-specific QAPI-generated code for multiple targets
>      coexist in the single binary.
> 
> As far as I can tell, your RFC series is an incomplete attempt at (2).
> 
> I gather you considered (3), but you dislike it for its bloat and
> possibly other reasons.  I sympathize; the QAPI-generated code is plenty
> bloated as it is, in good part to early design decisions (not mine).
> 
> Your "no noticeable differences" goal precludes (1).
> 
> Back to (2).  In C, replacing compile-time conditionals by run-time
> checks means replacing #if FOO by if (foo).  Such a transformation isn't
> possible in the QAPI schema.  To make it possible, we need to evolve the
> QAPI schema language.
> 
> docs/devel/qapi-code-gen.rst describes what we have:
> 
>          COND = STRING
>               | { 'all: [ COND, ... ] }
>               | { 'any: [ COND, ... ] }
>               | { 'not': COND }
> 
>      [....]
> 
>      The C code generated for the definition will then be guarded by an #if
>      preprocessing directive with an operand generated from that condition:
> 
>       * STRING will generate defined(STRING)
>       * { 'all': [COND, ...] } will generate (COND && ...)
>       * { 'any': [COND, ...] } will generate (COND || ...)
>       * { 'not': COND } will generate !COND
> 
> So, conditions are expression trees where the leaves are preprocessor
> symbols and the inner nodes are operators.
> 
> It's not quite obvious to me how to best evolve this to support run-time
> checks.
> 

After looking at the introspection code, I don't see any major blocker.
We need to keep some of existing "if", as they are based on config-host, 
and should apply.
We can introduce a new "available_if" (or any other name), which 
generates a runtime check when building the schema, or when serializing 
a struct.

This way, by modifying the .json with:
- if: 'TARGET_I386'
+ available_if: 'target_i386()'

This way, we keep the possibility to have ifdef, and we can expose at 
runtime based on available_if. So we can keep the exact same schema we 
have today per target.

> Whatever we choose should support generating Rust and Go as well.  Why?
> Rust usage in QEMU is growing, and we'll likely need to generate some
> Rust from the QAPI schema.  Victor Toso has been working on Go bindings
> for use in Go QMP client software.
> 

I don't see any blocker with that. If you mention generating Rust and Go 
from qapi json definitions, it's already dependent on C preprocessor 
because of ifdef constant. So it will have to be adapted anyway.
Having the same function (target_i386()) name through different 
languages is not something hard to achieve.

>>> The build system treats QAPI modules qapi/*-target.json as
>>> target-specific.  The .c files generated for them are compiled per
>>> target.  See qapi/meson.build.
>>>
>>> Only such target-specific modules can can use target-specific QAPI
>>> conditionals.  Use in target-independent modules will generate C that
>>> won't compile.
>>>
>>> Poisoned macros used in qapi/*-target.json:
>>>
>>>       CONFIG_KVM
>>>       TARGET_ARM
>>>       TARGET_I386
>>>       TARGET_LOONGARCH64
>>>       TARGET_MIPS
>>>       TARGET_PPC
>>>       TARGET_RISCV
>>>       TARGET_S390X
>>>
>>>>                              What we try to do here is to build them only once
>>>> instead.
>>>
>>> You're trying to eliminate target-specific QAPI conditionals.  Correct?
>>>
>>
>> Yes, but without impacting the list of commands exposed. Thus, it would
>> be needed to select at runtime to expose/register commands.
> 
> Conditionals affect more than just commands.
>

Thus, the proposal above to do the same for concerned struct members.

>>>> In the past, we identied that the best approach to solve this is to expose code
>>>> for all targets (thus removing all #if clauses), and stub missing
>>>> symbols for concerned targets.
>>>
>>> This affects QAPI/QMP introspection, i.e. the value of query-qmp-schema.
>>>
>>> Management applications can no longer use introspection to find out
>>> whether target-specific things are available.
>>>
>>
>> As asked on my previous email answering Daniel, would that be possible
>> to build the schema dynamically, so we can decide what to expose or not
>> introspection wise?
> 
> QAPI was designed to be compile-time static.  Revising such fundamental
> design assumptions is always fraught.  I can't give you a confident
> assessment now.  All I can offer you is my willingness to explore
> solutions.  See "really fancy" below.
> 
> Fun fact: we used to generate the value of query-qmp-schema as a single
> string.  We switched to the current, more bloated representation to
> support conditionals (commit 7d0f982bfbb).
>

It's nice to have this, and this is what would allow us to 
conditionnally include or not various definitions/commands/fields. I was 
a bit worried we would have a "static string", but was glad to find a 
static list instead.

>>> For instance, query-cpu-definitions is implemented for targets arm,
>>> i386, loongarch, mips, ppc, riscv, and s390x.  It initially was for
>>> fewer targets, and more targets followed one by one.  Still more may
>>> follow in the future.  Right now, management applications can use
>>> introspection to find out whether it is available.  That stops working
>>> when you make it available for all targets, stubbed out for the ones
>>> that don't (yet) implement it.
>>>
>>
>> I will repeat, just to be clear, I don't think exposing all commands is
>> a good idea.
>> The current series *does not* do this, simply because I didn't want to
>> huge work for nothing.
> 
> Got it.
> 
>>> Management applications may have to be adjusted for this.
>>>
>>> This is not an attempt to shoot down your approach.  I'm merely
>>> demonstrating limitations of your promise "if anyone notices a
>>> difference, it will be a bug."
>>>
>>
>> I stick to this promise :).
>>
>>> Now, we could get really fancy and try to keep introspection the same by
>>> applying conditionals dynamically somehow.  I.e. have the single binary
>>> return different introspection values depending on the actual guest's
>>> target.
>>>
>>> This requires fixing the target before introspection.  Unless this is
>>> somehow completely transparent (wrapper scripts, or awful hacks based on
>>> the binary's filename, perhaps), management applications may have to be
>>> adjusted to actually do that.
>>>
>>> Applies not just to introspection.  Consider query-cpu-definitions
>>> again.  It currently returns CPU definitions for *the* target.  What
>>> would a single binary's query-cpu-definitions return?  The CPU
>>> definitions for *all* its targets?  Management applications then receive
>>> CPUs that won't work, which may upset them.  To avoid noticable
>>> difference, we again have to fix the target before we look.
>>>
>>> Of course, "fixing the target" stops making sense once we move to
>>> heterogeneous machines with multiple targets.
>>>
>>
>> At this point, I don't have think about what should be the semantic when
>> we'll have multiple targets running simultaneously (expose the union,
>> restrict to the main arch, choose a third way).
> 
> We have to unless we make query-cpu-definitions fail or impossible to
> send while the target is still undecided.
> 
> Making it fail would violate the "no observable differences" goal.
> 
> The only path to true "no observable differences" I can see is to fix
> the target before the management application interacts with QEMU in any
> way.  This would make QMP commands (query-cpu-definitions,
> query-qmp-schema, ...) impossible to send before the target is fixed.
>

The current target will be set at the entry of main() in QEMU, so before 
the monitor is created. Thus, it will be unambiguous.

>>>> This series build QAPI generated code once, by removing all TARGET_{arch} and
>>>> CONFIG_KVM clauses. What it does *not* at the moment is:
>>>> - prevent target specific commands to be visible for all targets
>>>>     (see TODO comment on patch 2 explaining how to address this)
>>>> - nothing was done to hide all this from generated documentation
>>>
>>> For better or worse, generated documentation always contains everything.
>>>
>>
>> Fine for me, it makes sense, as the official documentation published,
>> which is what people will consume primarily, is for all targets.
>>
>>> An argument could be made for stripping out documentation for the stuff
>>> that isn't included in this build.
>>>
>>>>   From what I understood, the only thing that matters is to limit qmp commands
>>>> visible. Exposing enums, structure, or events is not a problem, since they
>>>> won't be used/triggered for non concerned targets. Please correct me if this is
>>>> wrong, and if there are unexpected consequences for libvirt or other consumers.
>>>
>>> I'm not sure what you mean by "to limit qmp commands visible".
>>>
>>> QAPI/QMP introspection has all commands and events, and all types
>>> reachable from them.  query-qmp-schema returns an array, where each
>>> array element describes one command, event, or type.  When a command,
>>> event, or type is conditional in the schema, the element is wrapped in
>>> the #if generated for the condition.
>>>
>>
>> After reading and answering to your valuable email, I definitely think
> 
> Thanks!
> 
>> the introspection schema we expose should be adapted, independently of
>> how we build QAPI code (i.e. using #ifdef TARGET or not).
>>
>> Is it something technically hard to achieve?
> 
> Unclear.  See "fundamental design assumptions" and "need to evolve the
> QAPI schema language" above.
> 
> If you want to learn more about introspection, I'd recommend
> docs/devel/qapi-code-gen.rst section "Client JSON Protocol
> introspection".
>

I'll give a try at conditioning all this by runtime checks, so you can 
review which changes it would create.

> [...]
> 

Regards,
Pierrick


  reply	other threads:[~2025-04-28 16:06 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-24 18:33 [RFC PATCH 0/3] single-binary: make QAPI generated files common Pierrick Bouvier
2025-04-24 18:33 ` [RFC PATCH 1/3] qapi: add weak stubs for target specific commands Pierrick Bouvier
2025-04-24 18:52   ` Pierrick Bouvier
2025-04-24 18:33 ` [RFC PATCH 2/3] qapi: always expose TARGET_* or CONFIG_KVM code Pierrick Bouvier
2025-04-24 18:33 ` [RFC PATCH 3/3] qapi: make all generated files common Pierrick Bouvier
2025-04-24 20:31   ` Philippe Mathieu-Daudé
2025-04-24 21:08   ` Richard Henderson
2025-04-24 20:43 ` [RFC PATCH 0/3] single-binary: make QAPI " Philippe Mathieu-Daudé
2025-04-24 21:15   ` Richard Henderson
2025-04-24 22:22   ` Pierrick Bouvier
2025-04-24 20:44 ` Philippe Mathieu-Daudé
2025-04-25  7:35 ` Daniel P. Berrangé
2025-04-25 20:39   ` Pierrick Bouvier
2025-04-28  8:37     ` Daniel P. Berrangé
2025-04-28 15:54       ` Pierrick Bouvier
2025-04-25 22:16   ` Philippe Mathieu-Daudé
2025-04-26  4:53     ` Markus Armbruster
2025-04-25 15:38 ` Markus Armbruster
2025-04-25 21:07   ` Pierrick Bouvier
2025-04-25 21:13     ` Pierrick Bouvier
2025-04-26  6:21     ` Markus Armbruster
2025-04-28 16:05       ` Pierrick Bouvier [this message]
2025-04-29  7:43         ` Markus Armbruster
2025-04-29  8:37           ` Daniel P. Berrangé
2025-04-29 19:26             ` Pierrick Bouvier
2025-05-07 11:21             ` Markus Armbruster
2025-04-29 19:15           ` Pierrick Bouvier
2025-05-07  7:55             ` Markus Armbruster
2025-05-07 11:32               ` Daniel P. Berrangé
2025-05-07 19:00                 ` Pierrick Bouvier
2025-05-07 18:54               ` Pierrick Bouvier
2025-04-28 10:25     ` Peter Krempa
2025-04-28 16:18       ` Pierrick Bouvier
2025-04-28  8:55   ` Peter Krempa
2025-04-28 11:07     ` Markus Armbruster
2025-04-28 12:48       ` Philippe Mathieu-Daudé
2025-04-28 16:35       ` Pierrick Bouvier
2025-04-29  8:23         ` Markus Armbruster
2025-04-29  9:20           ` Thomas Huth
2025-04-29  9:32             ` Daniel P. Berrangé
2025-04-29  9:39               ` Philippe Mathieu-Daudé
2025-04-29 19:48             ` Pierrick Bouvier
2025-04-30  5:40               ` Thomas Huth
2025-04-30  6:18                 ` Pierrick Bouvier
2025-04-29  9:35           ` Philippe Mathieu-Daudé
2025-04-29  9:47             ` Daniel P. Berrangé
2025-04-29 19:57             ` Pierrick Bouvier
2025-04-29 20:11               ` Pierrick Bouvier
2025-04-29 12:04           ` BALATON Zoltan
2025-04-28 18:14 ` Stefan Hajnoczi
2025-04-28 19:25   ` Pierrick Bouvier
2025-04-28 19:54     ` Stefan Hajnoczi
2025-04-28 21:35       ` Pierrick Bouvier
2025-05-07 23:19 ` Pierrick Bouvier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=25bb4527-f145-4d9c-8f91-a962bfa14a6f@linaro.org \
    --to=pierrick.bouvier@linaro.org \
    --cc=alex.bennee@linaro.org \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=devel@lists.libvirt.org \
    --cc=jsnow@redhat.com \
    --cc=michael.roth@amd.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=richard.henderson@linaro.org \
    --cc=stefanha@redhat.com \
    --cc=thuth@redhat.com \
    --cc=victortoso@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).