From: Markus Armbruster <armbru@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: Zhao Liu <zhao1.liu@intel.com>,
qemu-devel@nongnu.org, marcandre.lureau@redhat.com,
qemu-rust@nongnu.org, mkletzan@redhat.com
Subject: Re: [PATCH preview 0/3] reviving minimal QAPI generation from 2021
Date: Wed, 18 Jun 2025 16:25:14 +0200 [thread overview]
Message-ID: <877c19nn3p.fsf@pond.sub.org> (raw)
In-Reply-To: <CABgObfa+w3pcYhFnO6ETxSfoNiNU=+_8WcW6dE8dkUrbt6darw@mail.gmail.com> (Paolo Bonzini's message of "Thu, 12 Jun 2025 12:24:44 +0200")
I don't know enough about Rust/serde to give advice. I do know how to
make a fool of myself by asking dumb questions.
Paolo Bonzini <pbonzini@redhat.com> writes:
> On Wed, Jun 11, 2025 at 10:57 AM Paolo Bonzini <pbonzini@redhat.com> wrote:
>> Yes. If using serde the implementation of the traits is very small,
>> and basically the same for all types. If not using serde, it would
>> need some (or most) of the infrastructure in Marc-André's original
>> series.
>
> Looking more at it, the Rust->QObject and QObject->Rust parts *can* be
> done with serde (following the model of serde_json's Value
> (de)serializer) but the Rust<->C part has a problem.
>
> To recap, Rust->C is the serialization and corresponds to output
> visitors. C->Rust is the deserialization and corresponds to input
> visitors.
>
> For serialization, serde has a push model where the generated code
> looks like this:
>
> let mut state =
> Serializer::serialize_struct(serializer, "S", 2);
> SerializeStruct::serialize_field(&mut state, "a", &self.a)?;
> SerializeStruct::serialize_field(&mut state, "b", &self.b)?;
> SerializeStruct::end(state)
>
> whereas QAPI has a pull model where visit_type_* drives the process
> and requests the fields one by one.
>
> For deserialization, serde has a pull model where the generated code
> asks for the field names one by one:
>
> fn visit_map<__A>(self, mut __map: __A)
> while let Some(key) =
> MapAccess::next_key::<__Field>(&mut __map)? {
> match __key { ... }
> }
> }
>
> whereas QAPI has a push model where visit_type_* again drives the
> process and sends fields one by one.
>
> For commands this is not a problem because the real underlying
> transformation is QObject->QObject and the intermediate steps (to and
> from QObject) can use serde.
Are you talking about commands implemented in Rust?
The existing data flow is roughly like this (I'm simplifying):
1. Parse JSON text into request QObject, pass to QMP core
2. Extract command name string and argument QDict
3. Look up generated command marshaller / unmarshaller, pass argument
QDict to it
4. Unmarshall argument QDict with the QObject input visitor and
generated visit_type_ARG()
5. Pass the C arguments to the handwritten command handler, receive the
C return value
6. Marshall the return value into a QObject with the QObject output
visitor and generated visit_type_RET(), return it to QMP core
7. Insert it into a response QObject
8. Unparse response QObject into JSON text
How would a Serde flow look like?
> However, QOM property getters/setters (especially, but not
> exclusively, for properties with compound types) remain a problem
> since these use callbacks with a Visitor* argument.
object_property_set() takes the new property value wrapped in an input
visitor. The property setter extracts it using visit_type_FOOs() with
this input visitor as it sees fit. Ideally, it uses exactly
visit_type_PROPTYPE().
object_property_get() takes an output visitor to be wrapped it around
the property value. The property getter inserts it using
visit_type_FOOs() with this output visitor as it sees fit. Ideally, it
uses exactly visit_type_PROPTYPE().
We sometimes use a QObject input / output visitor, and sometimes a
string input / output visitor. The latter come with restrictions, and
are evolutionary dead ends.
The QObject visitors wrap a QObject, the string visitors wrap a string
(d'oh).
> I see three
> possibilities:
>
> 1) everything is done through an intermediate QObject step (e.g. for a
> setter: Visitor->QObject with an input visitor, and QObject->Rust with
> serde deserialization).
> + easy, Rust only sees serde
> + QMP commands use a single conversion step
> - inefficient
>
> 2) everything is done through an intermediate C step (e.g. for a
> setter: Visitor->C with a visit_type_* function, and C->Rust with
> generated code that does not need to use serde). There is still a
> double conversion step, but it's more efficient than option 1
> + one framework (visitor)
> - double conversion for the QMP commands
> - lots of generated code
>
> 3) generating a Rust visit_type_* implementation as well, either in
> qapi-gen (3a) or through a procedural macro (3b). This should not be
> hard to write but it would remove a lot of the advantages from using
> serde.
> + efficient
> + preserves single conversion for QMP commands
> - two frameworks
I'm afraid this is too terse for ignorant me.
> I am leaning towards option 1, i.e. keep using serde but only cover
> conversions to and from QObject. The reason is that one future usecase
> for Rust in QEMU is the UEFI variable store; that one also has some
> Rust<->JSON conversions and could be served by either QObject or
> serde_json. Either way, it'd be nice for the UEFI variable store to
> remain within the Rust serde ecosystem and allow sharing code between
> QEMU and Coconut SVSM. But I'm not so sure...
>
> Paolo
next prev parent reply other threads:[~2025-06-18 14:25 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-05 10:11 [PATCH preview 0/3] reviving minimal QAPI generation from 2021 Paolo Bonzini
2025-06-05 10:11 ` [PATCH 1/3] rust: make TryFrom macro more resilient Paolo Bonzini
2025-06-10 13:26 ` Marc-André Lureau
2025-06-10 15:52 ` Zhao Liu
2025-06-05 10:11 ` [PATCH 2/3] scripts/qapi: add QAPISchemaIfCond.rsgen() Paolo Bonzini
2025-06-10 13:33 ` Marc-André Lureau
2025-06-05 10:11 ` [PATCH 3/3] scripts/qapi: generate high-level Rust bindings Paolo Bonzini
2025-06-11 8:09 ` Zhao Liu
2025-06-10 13:53 ` [PATCH preview 0/3] reviving minimal QAPI generation from 2021 Marc-André Lureau
2025-06-10 16:10 ` Paolo Bonzini
2025-06-11 8:13 ` Zhao Liu
2025-06-11 8:57 ` Paolo Bonzini
2025-06-12 10:24 ` Paolo Bonzini
2025-06-13 5:57 ` Zhao Liu
2025-06-16 8:55 ` Paolo Bonzini
2025-06-18 14:25 ` Markus Armbruster [this message]
2025-06-18 17:36 ` Paolo Bonzini
2025-06-23 12:52 ` Markus Armbruster
2025-07-02 19:09 ` Paolo Bonzini
2025-06-17 7:49 ` Markus Armbruster
2025-06-18 8:27 ` Paolo Bonzini
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=877c19nn3p.fsf@pond.sub.org \
--to=armbru@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=mkletzan@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-rust@nongnu.org \
--cc=zhao1.liu@intel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.