migration: Fix iocs leaks during file and fd migration

The memory for the io channels is being leaked in three different ways
during file migration:

1) if the offset check fails we never drop the ioc reference;

2) we allocate an extra channel for no reason;

3) if multifd is enabled but channel creation fails when calling
   dup(), we leave the previous channels around along with the glib
   polling;

Fix all issues by restructuring the code to first allocate the
channels and only register the watches when all channels have been
created.

For multifd, the file and fd migrations can share code because both
are backed by a QIOChannelFile. For the non-multifd case, the fd needs
to be separate because it is backed by a QIOChannelSocket.

Fixes: 2dd7ee7a51 ("migration/multifd: Add incoming QIOChannelFile support")
Fixes: decdc76772 ("migration/multifd: Add mapped-ram support to fd: URI")
Reported-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
Link: https://lore.kernel.org/r/20240313212824.16974-2-farosas@suse.de
Signed-off-by: Peter Xu <peterx@redhat.com>
This commit is contained in:
Fabiano Rosas 2024-03-13 18:28:23 -03:00 committed by Peter Xu
parent 20e6b15653
commit 74228c598f
3 changed files with 46 additions and 42 deletions

View File

@ -18,6 +18,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "channel.h" #include "channel.h"
#include "fd.h" #include "fd.h"
#include "file.h"
#include "migration.h" #include "migration.h"
#include "monitor/monitor.h" #include "monitor/monitor.h"
#include "io/channel-file.h" #include "io/channel-file.h"
@ -80,7 +81,6 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc,
void fd_start_incoming_migration(const char *fdname, Error **errp) void fd_start_incoming_migration(const char *fdname, Error **errp)
{ {
QIOChannel *ioc; QIOChannel *ioc;
QIOChannelFile *fioc;
int fd = monitor_fd_param(monitor_cur(), fdname, errp); int fd = monitor_fd_param(monitor_cur(), fdname, errp);
if (fd == -1) { if (fd == -1) {
return; return;
@ -94,26 +94,13 @@ void fd_start_incoming_migration(const char *fdname, Error **errp)
return; return;
} }
qio_channel_set_name(ioc, "migration-fd-incoming");
qio_channel_add_watch_full(ioc, G_IO_IN,
fd_accept_incoming_migration,
NULL, NULL,
g_main_context_get_thread_default());
if (migrate_multifd()) { if (migrate_multifd()) {
int channels = migrate_multifd_channels(); file_create_incoming_channels(ioc, errp);
} else {
while (channels--) { qio_channel_set_name(ioc, "migration-fd-incoming");
fioc = qio_channel_file_new_dupfd(fd, errp); qio_channel_add_watch_full(ioc, G_IO_IN,
if (!fioc) { fd_accept_incoming_migration,
return; NULL, NULL,
} g_main_context_get_thread_default());
qio_channel_set_name(ioc, "migration-fd-incoming");
qio_channel_add_watch_full(QIO_CHANNEL(fioc), G_IO_IN,
fd_accept_incoming_migration,
NULL, NULL,
g_main_context_get_thread_default());
}
} }
} }

View File

@ -115,13 +115,46 @@ static gboolean file_accept_incoming_migration(QIOChannel *ioc,
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
void file_create_incoming_channels(QIOChannel *ioc, Error **errp)
{
int i, fd, channels = 1;
g_autofree QIOChannel **iocs = NULL;
if (migrate_multifd()) {
channels += migrate_multifd_channels();
}
iocs = g_new0(QIOChannel *, channels);
fd = QIO_CHANNEL_FILE(ioc)->fd;
iocs[0] = ioc;
for (i = 1; i < channels; i++) {
QIOChannelFile *fioc = qio_channel_file_new_dupfd(fd, errp);
if (!fioc) {
while (i) {
object_unref(iocs[--i]);
}
return;
}
iocs[i] = QIO_CHANNEL(fioc);
}
for (i = 0; i < channels; i++) {
qio_channel_set_name(iocs[i], "migration-file-incoming");
qio_channel_add_watch_full(iocs[i], G_IO_IN,
file_accept_incoming_migration,
NULL, NULL,
g_main_context_get_thread_default());
}
}
void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp)
{ {
g_autofree char *filename = g_strdup(file_args->filename); g_autofree char *filename = g_strdup(file_args->filename);
QIOChannelFile *fioc = NULL; QIOChannelFile *fioc = NULL;
uint64_t offset = file_args->offset; uint64_t offset = file_args->offset;
int channels = 1;
int i = 0;
trace_migration_file_incoming(filename); trace_migration_file_incoming(filename);
@ -132,28 +165,11 @@ void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp)
if (offset && if (offset &&
qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) {
object_unref(OBJECT(fioc));
return; return;
} }
if (migrate_multifd()) { file_create_incoming_channels(QIO_CHANNEL(fioc), errp);
channels += migrate_multifd_channels();
}
do {
QIOChannel *ioc = QIO_CHANNEL(fioc);
qio_channel_set_name(ioc, "migration-file-incoming");
qio_channel_add_watch_full(ioc, G_IO_IN,
file_accept_incoming_migration,
NULL, NULL,
g_main_context_get_thread_default());
fioc = qio_channel_file_new_dupfd(fioc->fd, errp);
if (!fioc) {
break;
}
} while (++i < channels);
} }
int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov,

View File

@ -20,6 +20,7 @@ void file_start_outgoing_migration(MigrationState *s,
int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp);
void file_cleanup_outgoing_migration(void); void file_cleanup_outgoing_migration(void);
bool file_send_channel_create(gpointer opaque, Error **errp); bool file_send_channel_create(gpointer opaque, Error **errp);
void file_create_incoming_channels(QIOChannel *ioc, Error **errp);
int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov,
int niov, RAMBlock *block, Error **errp); int niov, RAMBlock *block, Error **errp);
int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp); int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp);