migration/rdma.c: Swap synchronize_rcu for call_rcu

This fixes a deadlock that can occur on the migration source after
a failed RDMA migration;  as the source tries to cleanup it
clears a pair of pointers and uses synchronize_rcu to wait; this
is happening on the main thread.  With the CPUs running
a CPU thread can be an rcu reader and attempt to grab the main lock
(kvm_handle_io->address_space_write->flatview_write->flatview_write_continue->
prepare_mmio_access->qemu_mutex_lock_iothread_impl)

Replace the synchronize_rcu with a call_rcu to postpone the freeing.

Fixes: 74637e6f08 ("migration: implement bi-directional RDMA QIOChannel")

( https://bugzilla.redhat.com/show_bug.cgi?id=1746787 )

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-Id: <20190913163507.1403-3-dgilbert@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
This commit is contained in:
Dr. David Alan Gilbert 2019-09-13 17:35:07 +01:00
parent de8434a35a
commit d46a4847ca

View File

@ -3017,11 +3017,35 @@ static void qio_channel_rdma_set_aio_fd_handler(QIOChannel *ioc,
} }
} }
struct rdma_close_rcu {
struct rcu_head rcu;
RDMAContext *rdmain;
RDMAContext *rdmaout;
};
/* callback from qio_channel_rdma_close via call_rcu */
static void qio_channel_rdma_close_rcu(struct rdma_close_rcu *rcu)
{
if (rcu->rdmain) {
qemu_rdma_cleanup(rcu->rdmain);
}
if (rcu->rdmaout) {
qemu_rdma_cleanup(rcu->rdmaout);
}
g_free(rcu->rdmain);
g_free(rcu->rdmaout);
g_free(rcu);
}
static int qio_channel_rdma_close(QIOChannel *ioc, static int qio_channel_rdma_close(QIOChannel *ioc,
Error **errp) Error **errp)
{ {
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
RDMAContext *rdmain, *rdmaout; RDMAContext *rdmain, *rdmaout;
struct rdma_close_rcu *rcu = g_new(struct rdma_close_rcu, 1);
trace_qemu_rdma_close(); trace_qemu_rdma_close();
rdmain = rioc->rdmain; rdmain = rioc->rdmain;
@ -3034,18 +3058,9 @@ static int qio_channel_rdma_close(QIOChannel *ioc,
atomic_rcu_set(&rioc->rdmaout, NULL); atomic_rcu_set(&rioc->rdmaout, NULL);
} }
synchronize_rcu(); rcu->rdmain = rdmain;
rcu->rdmaout = rdmaout;
if (rdmain) { call_rcu(rcu, qio_channel_rdma_close_rcu, rcu);
qemu_rdma_cleanup(rdmain);
}
if (rdmaout) {
qemu_rdma_cleanup(rdmaout);
}
g_free(rdmain);
g_free(rdmaout);
return 0; return 0;
} }