From: Kevin Wolf <kwolf@redhat.com>
To: Stefan Hajnoczi <stefanha@redhat.com>
Cc: qemu-devel@nongnu.org,
"Dr. David Alan Gilbert" <dave@treblig.org>,
"Eduardo Habkost" <eduardo@habkost.net>,
pbonzini@redhat.com, "Markus Armbruster" <armbru@redhat.com>,
"Eric Blake" <eblake@redhat.com>,
"Maxim Levitsky" <mlevitsk@redhat.com>,
"Daniel P. Berrangé" <berrange@redhat.com>
Subject: Re: [RFC 3/3] qmp: make qmp_device_add() a coroutine
Date: Tue, 12 Sep 2023 18:47:04 +0200 [thread overview]
Message-ID: <ZQCWCEzSj8P1sdyW@redhat.com> (raw)
In-Reply-To: <20230906190141.1286893-4-stefanha@redhat.com>
Am 06.09.2023 um 21:01 hat Stefan Hajnoczi geschrieben:
> It is not safe to call drain_call_rcu() from qmp_device_add() because
> some call stacks are not prepared for drain_call_rcu() to drop the Big
> QEMU Lock (BQL).
>
> For example, device emulation code is protected by the BQL but when it
> calls aio_poll() -> ... -> qmp_device_add() -> drain_call_rcu() then the
> BQL is dropped. See bz#2215192 below for a concrete bug of this type.
>
> Another limitation of drain_call_rcu() is that it cannot be invoked
> within an RCU read-side critical section since the reclamation phase
> cannot complete until the end of the critical section. Unfortunately,
> call stacks have been seen where this happens (see bz#2214985 below).
>
> Switch to call_drain_rcu_co() to avoid these problems. This requires
> making qmp_device_add() a coroutine. qdev_device_add() is not designed
> to be called from coroutines, so it must be invoked from a BH and then
> switch back to the coroutine.
>
> Fixes: 7bed89958bfbf40df9ca681cefbdca63abdde39d ("device_core: use drain_call_rcu in in qmp_device_add")
> Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2215192
> Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2214985
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Can you please include the relevant information directly in the commit
message instead of only referencing Bugzilla? Both bugs only contain
half of the story - I'm not even sure if the link with the stack trace
is publically accessible - and then I think you got some information
only from reproducing it yourself, and this information is missing from
the bug reports. (The other question is how long the information will
still be available in Bugzilla.)
> qapi/qdev.json | 1 +
> include/monitor/qdev.h | 3 ++-
> monitor/qmp-cmds.c | 2 +-
> softmmu/qdev-monitor.c | 34 ++++++++++++++++++++++++++++++----
> hmp-commands.hx | 1 +
> 5 files changed, 35 insertions(+), 6 deletions(-)
>
> diff --git a/qapi/qdev.json b/qapi/qdev.json
> index 6bc5a733b8..78e9d7f7b8 100644
> --- a/qapi/qdev.json
> +++ b/qapi/qdev.json
> @@ -79,6 +79,7 @@
> ##
> { 'command': 'device_add',
> 'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
> + 'coroutine': true,
> 'gen': false, # so we can get the additional arguments
> 'features': ['json-cli', 'json-cli-hotplug'] }
>
> diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h
> index 1d57bf6577..1fed9eb9ea 100644
> --- a/include/monitor/qdev.h
> +++ b/include/monitor/qdev.h
> @@ -5,7 +5,8 @@
>
> void hmp_info_qtree(Monitor *mon, const QDict *qdict);
> void hmp_info_qdm(Monitor *mon, const QDict *qdict);
> -void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp);
> +void coroutine_fn
> +qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp);
>
> int qdev_device_help(QemuOpts *opts);
> DeviceState *qdev_device_add(QemuOpts *opts, Error **errp);
> diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
> index b0f948d337..a7419226fe 100644
> --- a/monitor/qmp-cmds.c
> +++ b/monitor/qmp-cmds.c
> @@ -202,7 +202,7 @@ static void __attribute__((__constructor__)) monitor_init_qmp_commands(void)
> qmp_init_marshal(&qmp_commands);
>
> qmp_register_command(&qmp_commands, "device_add",
> - qmp_device_add, 0, 0);
> + qmp_device_add, QCO_COROUTINE, 0);
>
> QTAILQ_INIT(&qmp_cap_negotiation_commands);
> qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
> diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c
> index 74f4e41338..85ae62f7cf 100644
> --- a/softmmu/qdev-monitor.c
> +++ b/softmmu/qdev-monitor.c
> @@ -839,8 +839,28 @@ void hmp_info_qdm(Monitor *mon, const QDict *qdict)
> qdev_print_devinfos(true);
> }
>
> -void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
> +typedef struct {
> + Coroutine *co;
> + QemuOpts *opts;
> + Error **errp;
> + DeviceState *dev;
> +} QmpDeviceAdd;
> +
> +static void qmp_device_add_bh(void *opaque)
> {
> + QmpDeviceAdd *data = opaque;
> +
> + data->dev = qdev_device_add(data->opts, data->errp);
> + aio_co_wake(data->co);
> +}
> +
> +void coroutine_fn
> +qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
> +{
> + QmpDeviceAdd data = {
> + .co = qemu_coroutine_self(),
> + .errp = errp,
> + };
> QemuOpts *opts;
> DeviceState *dev;
>
> @@ -852,7 +872,13 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
> qemu_opts_del(opts);
> return;
> }
> - dev = qdev_device_add(opts, errp);
> +
> + /* Perform qdev_device_add() call outside coroutine context */
> + data.opts = opts;
> + aio_bh_schedule_oneshot(qemu_coroutine_get_aio_context(data.co),
> + qmp_device_add_bh, &data);
> + qemu_coroutine_yield();
> + dev = data.dev;
I wonder if we should make no_co_wrapper etc. available outside of the
block layer, so we could just declare a qdev_co_device_add() and call it
here and the code would automatically be generated.
This doesn't work today because the script generates only a single
source file for all wrappers, which is linked with all of the tools. So
putting qdev functions there would make the build fail.
I had a similar case in the virtio_load() fix where I decided to write
the wrapper manually instead. But having two cases in such a short time
frame might be a sign that we actually have enough potential users that
making the generator work for it would be worth it.
Kevin
next prev parent reply other threads:[~2023-09-12 16:47 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-06 19:01 [RFC 0/3] qmp: make qmp_device_add() a coroutine Stefan Hajnoczi
2023-09-06 19:01 ` [RFC 1/3] hmp: avoid the nested event loop in handle_hmp_command() Stefan Hajnoczi
2023-09-07 1:06 ` Dr. David Alan Gilbert
2023-09-07 14:04 ` Stefan Hajnoczi
2023-09-07 14:07 ` Dr. David Alan Gilbert
2023-09-07 15:34 ` Stefan Hajnoczi
2023-09-07 20:53 ` Dr. David Alan Gilbert
2023-09-07 21:25 ` Stefan Hajnoczi
2023-09-06 19:01 ` [RFC 2/3] rcu: add drain_call_rcu_co() API Stefan Hajnoczi
2023-09-12 16:36 ` Kevin Wolf
2023-09-12 16:52 ` Stefan Hajnoczi
2023-09-06 19:01 ` [RFC 3/3] qmp: make qmp_device_add() a coroutine Stefan Hajnoczi
2023-09-12 16:47 ` Kevin Wolf [this message]
2023-09-12 17:04 ` Stefan Hajnoczi
2023-09-07 11:28 ` [RFC 0/3] " Paolo Bonzini
2023-09-07 14:00 ` Stefan Hajnoczi
2023-09-07 14:25 ` Paolo Bonzini
2023-09-07 15:29 ` Stefan Hajnoczi
2023-09-12 17:08 ` Kevin Wolf
2023-09-13 11:38 ` 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=ZQCWCEzSj8P1sdyW@redhat.com \
--to=kwolf@redhat.com \
--cc=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=dave@treblig.org \
--cc=eblake@redhat.com \
--cc=eduardo@habkost.net \
--cc=mlevitsk@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@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).