From: Peter Xu <peterx@redhat.com>
To: Avihai Horon <avihaih@nvidia.com>
Cc: Fabiano Rosas <farosas@suse.de>, qemu-devel@nongnu.org
Subject: Re: [PATCH 5/5] migration/multifd: Add a synchronization point for channel creation
Date: Mon, 5 Feb 2024 20:53:24 +0800 [thread overview]
Message-ID: <ZcDaRCZdAurisoyj@x1n> (raw)
In-Reply-To: <6bd7f665-23d9-48d5-9f79-0b012e3a6205@nvidia.com>
On Mon, Feb 05, 2024 at 01:10:14PM +0200, Avihai Horon wrote:
>
> On 05/02/2024 8:20, Peter Xu wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > On Fri, Feb 02, 2024 at 04:11:28PM -0300, Fabiano Rosas wrote:
> > > It is possible that one of the multifd channels fails to be created at
> > > multifd_new_send_channel_async() while the rest of the channel
> > > creation tasks are still in flight.
> > >
> > > This could lead to multifd_save_cleanup() executing the
> > > qemu_thread_join() loop too early and not waiting for the threads
> > > which haven't been created yet, leading to the freeing of resources
> > > that the newly created threads will try to access and crash.
> > >
> > > Add a synchronization point after which there will be no attempts at
> > > thread creation and therefore calling multifd_save_cleanup() past that
> > > point will ensure it properly waits for the threads.
> > >
> > > A note about performance: Prior to this patch, if a channel took too
> > > long to be established, other channels could finish connecting first
> > > and already start taking load. Now we're bounded by the
> > > slowest-connecting channel.
> > Yes, I think this should (hopefully!) be fine.
> >
> > > Signed-off-by: Fabiano Rosas <farosas@suse.de>
> > > ---
> > > migration/multifd.c | 67 +++++++++++++++++++++++++--------------------
> > > 1 file changed, 37 insertions(+), 30 deletions(-)
> > >
> > > diff --git a/migration/multifd.c b/migration/multifd.c
> > > index 1851206352..888ac8b05d 100644
> > > --- a/migration/multifd.c
> > > +++ b/migration/multifd.c
> > > @@ -360,6 +360,11 @@ struct {
> > > MultiFDPages_t *pages;
> > > /* global number of generated multifd packets */
> > > uint64_t packet_num;
> > > + /*
> > > + * Synchronization point past which no more channels will be
> > > + * created.
> > > + */
> > > + QemuSemaphore channels_created;
> > > /* send channels ready */
> > > QemuSemaphore channels_ready;
> > > /*
> > > @@ -561,6 +566,7 @@ void multifd_save_cleanup(void)
> > > error_free(local_err);
> > > }
> > > }
> > > + qemu_sem_destroy(&multifd_send_state->channels_created);
> > > qemu_sem_destroy(&multifd_send_state->channels_ready);
> > > g_free(multifd_send_state->params);
> > > multifd_send_state->params = NULL;
> > > @@ -787,13 +793,6 @@ static void multifd_tls_outgoing_handshake(QIOTask *task,
> > > trace_multifd_tls_outgoing_handshake_error(ioc, error_get_pretty(err));
> > >
> > > migrate_set_error(migrate_get_current(), err);
> > > - /*
> > > - * Error happen, mark multifd_send_thread status as 'quit' although it
> > > - * is not created, and then tell who pay attention to me.
> > > - */
> > > - p->quit = true;
> > > - qemu_sem_post(&multifd_send_state->channels_ready);
> > > - qemu_sem_post(&p->sem_sync);
> > > error_free(err);
> > > }
> > >
> > > @@ -862,39 +861,37 @@ static bool multifd_channel_connect(MultiFDSendParams *p,
> > > return true;
> > > }
> > >
> > > -static void multifd_new_send_channel_cleanup(MultiFDSendParams *p,
> > > - QIOChannel *ioc, Error *err)
> > > -{
> > > - migrate_set_error(migrate_get_current(), err);
> > > - /* Error happen, we need to tell who pay attention to me */
> > > - qemu_sem_post(&multifd_send_state->channels_ready);
> > > - qemu_sem_post(&p->sem_sync);
> > > - /*
> > > - * Although multifd_send_thread is not created, but main migration
> > > - * thread need to judge whether it is running, so we need to mark
> > > - * its status.
> > > - */
> > > - p->quit = true;
> > > - object_unref(OBJECT(ioc));
> > > - error_free(err);
> > > -}
> > > -
> > > static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
> > > {
> > > MultiFDSendParams *p = opaque;
> > > QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task));
> > > Error *local_err = NULL;
> > > + bool ret;
> > >
> > > trace_multifd_new_send_channel_async(p->id);
> > > - if (!qio_task_propagate_error(task, &local_err)) {
> > > - qio_channel_set_delay(ioc, false);
> > > - if (multifd_channel_connect(p, ioc, &local_err)) {
> > > - return;
> > > - }
> > > +
> > > + if (qio_task_propagate_error(task, &local_err)) {
> > > + ret = false;
> > > + goto out;
> > > + }
> > > +
> > > + qio_channel_set_delay(ioc, false);
> > > + ret = multifd_channel_connect(p, ioc, &local_err);
> > > +
> > > +out:
> > > + /*
> > > + * Here we're not interested whether creation succeeded, only that
> > > + * it happened at all.
> > > + */
> > > + qemu_sem_post(&multifd_send_state->channels_created);
> > > + if (ret) {
> > > + return;
> > > }
> > >
> > > trace_multifd_new_send_channel_async_error(p->id, local_err);
> > > - multifd_new_send_channel_cleanup(p, ioc, local_err);
> > > + migrate_set_error(migrate_get_current(), local_err);
> > > + object_unref(OBJECT(ioc));
> > > + error_free(local_err);
> > > }
> > >
> > > static void multifd_new_send_channel_create(gpointer opaque)
> > > @@ -918,6 +915,7 @@ bool multifd_save_setup(void)
> > > multifd_send_state = g_malloc0(sizeof(*multifd_send_state));
> > > multifd_send_state->params = g_new0(MultiFDSendParams, thread_count);
> > > multifd_send_state->pages = multifd_pages_init(page_count);
> > > + qemu_sem_init(&multifd_send_state->channels_created, 0);
> > > qemu_sem_init(&multifd_send_state->channels_ready, 0);
> > > qatomic_set(&multifd_send_state->exiting, 0);
> > > multifd_send_state->ops = multifd_ops[migrate_multifd_compression()];
> > > @@ -953,6 +951,15 @@ bool multifd_save_setup(void)
> > > multifd_new_send_channel_create(p);
> > > }
> > >
> > > + /*
> > > + * Wait until channel creation has started for all channels. The
> > > + * creation can still fail, but no more channels will be created
> > > + * past this point.
> > > + */
> > Let me double check with you here on the TLS use case.
> >
> > IIUC we still can have more channels to be created if TLS is enabled: we
> > notify the sem as long as the handshake thread is created, then the
> > handshake thread can further create the tls-armed iochannel? However I
> > think I get your point, and that is fine, because if that is the case, even
> > though this loop can complete before tls further creates the final channel,
> > we'll still see tls_thread_created==true and join() that tls thread first,
> > then further we'll join() the next multifd thread even if a new one will
> > pop up, or if it failed then nothing to join besides the tls thread.
> >
> > I'm not sure whether Avihai has any input, I think this can be a good idea
> > indeed.
>
> Nothing special, my understanding of this is the same as yours.
> This fix looks solid.
>
> > there's a dependency chain on the ordering if my above
> > undertanding is correct; we may want to document this somewhere, perhaps
> > right here on the chaining of threads and how we handle that?
>
> I agree, this is subtle and may deserve a small note or hint.
IMHO it'll be always better to be verbose on these than "not enough info".
One thing I'd also like a comment is now the order is a must to firstly
join tls threads then multifd threads, not vice versa, not anymore. We may
want a comment above the two join()s there to state this hard requirement.
>
> >
> > This may not allow a concurrent migrate_cancel to respond, but I assume
> > this is good enough; the migrate_cancel request is indeed at least so far
> > something I made up, but not a request from anyone. We can leave that for
> > later and fix the race / crash first. This seems to be a complete fix from
> > that regard.
> >
> > > + for (i = 0; i < thread_count; i++) {
> > > + qemu_sem_wait(&multifd_send_state->channels_created);
> > > + }
> > > +
> > > for (i = 0; i < thread_count; i++) {
> > > MultiFDSendParams *p = &multifd_send_state->params[i];
> > >
> > > --
> > > 2.35.3
> > >
> > One other note is I think this will also deserve a cc: stable? But then
> > it'll mean all patch 3/4 will also need to copy stable to make Michael's
> > life easier.
> >
> > Let's also copy Dan when repost; after all he more or less owns the TLS
> > part.
> >
> > Thanks!
> >
> > --
> > Peter Xu
> >
>
--
Peter Xu
next prev parent reply other threads:[~2024-02-05 12:54 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-02 19:11 [PATCH 0/5] migration/multifd: Fix channel creation vs. cleanup races Fabiano Rosas
2024-02-02 19:11 ` [PATCH 1/5] migration/multifd: Join the TLS thread Fabiano Rosas
2024-02-05 5:32 ` Peter Xu
2024-02-02 19:11 ` [PATCH 2/5] migration/multifd: Remove p->running Fabiano Rosas
2024-02-05 5:34 ` Peter Xu
2024-02-02 19:11 ` [PATCH 3/5] migration/multifd: Move multifd_save_setup error handling in to the function Fabiano Rosas
2024-02-05 5:52 ` Peter Xu
2024-02-02 19:11 ` [PATCH 4/5] migration/multifd: Move multifd_save_setup into migration thread Fabiano Rosas
2024-02-05 5:52 ` Peter Xu
2024-02-02 19:11 ` [PATCH 5/5] migration/multifd: Add a synchronization point for channel creation Fabiano Rosas
2024-02-05 6:20 ` Peter Xu
2024-02-05 11:10 ` Avihai Horon
2024-02-05 12:53 ` Peter Xu [this message]
2024-02-05 15:41 ` Fabiano Rosas
2024-02-05 6:32 ` [PATCH 0/5] migration/multifd: Fix channel creation vs. cleanup races Peter Xu
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=ZcDaRCZdAurisoyj@x1n \
--to=peterx@redhat.com \
--cc=avihaih@nvidia.com \
--cc=farosas@suse.de \
--cc=qemu-devel@nongnu.org \
/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).