The core yank code is strict about balanced registering and unregistering of yank functions.
This creates a difficulty because the migration code registers one yank function per QIOChannel, but each QIOChannel can be referenced by more than one QEMUFile. The yank function should not be removed until all QEMUFiles have been closed. Keep a reference count of how many QEMUFiles are using a QIOChannel that has a yank function. Only unregister the yank function when all QEMUFiles have been closed. This improves the current code by removing the need for the programmer to know which QEMUFile is the last one to be cleaned up and fixes the theoretical issue of removing the yank function while another QEMUFile could still be using the ioc and require a yank. Signed-off-by: Fabiano Rosas <faro...@suse.de> --- migration/yank_functions.c | 81 ++++++++++++++++++++++++++++++++++---- migration/yank_functions.h | 8 ++++ 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/migration/yank_functions.c b/migration/yank_functions.c index 979e60c762..afdeef30ff 100644 --- a/migration/yank_functions.c +++ b/migration/yank_functions.c @@ -10,9 +10,32 @@ #include "qemu/osdep.h" #include "io/channel.h" #include "yank_functions.h" +#include "qemu/lockable.h" #include "qemu/yank.h" #include "qemu-file.h" +static QemuMutex ioc_list_lock; +static QLIST_HEAD(, Yankable) yankable_ioc_list + = QLIST_HEAD_INITIALIZER(yankable_ioc_list); + +static void __attribute__((constructor)) ioc_list_lock_init(void) +{ + qemu_mutex_init(&ioc_list_lock); +} + +static void yankable_ref(Yankable *yankable) +{ + assert(yankable->refcnt > 0); + yankable->refcnt++; + assert(yankable->refcnt < INT_MAX); +} + +static void yankable_unref(Yankable *yankable) +{ + assert(yankable->refcnt > 0); + yankable->refcnt--; +} + void migration_yank_iochannel(void *opaque) { QIOChannel *ioc = QIO_CHANNEL(opaque); @@ -28,20 +51,62 @@ static bool migration_ioc_yank_supported(QIOChannel *ioc) void migration_ioc_register_yank(QIOChannel *ioc) { - if (migration_ioc_yank_supported(ioc)) { - yank_register_function(MIGRATION_YANK_INSTANCE, - migration_yank_iochannel, - ioc); + Yankable *new, *entry; + + if (!ioc || !migration_ioc_yank_supported(ioc)) { + return; } + + WITH_QEMU_LOCK_GUARD(&ioc_list_lock) { + QLIST_FOREACH(entry, &yankable_ioc_list, next) { + if (entry->opaque == ioc) { + yankable_ref(entry); + return; + } + } + + new = g_new0(Yankable, 1); + new->refcnt = 1; + new->opaque = ioc; + object_ref(ioc); + + QLIST_INSERT_HEAD(&yankable_ioc_list, new, next); + } + + yank_register_function(MIGRATION_YANK_INSTANCE, + migration_yank_iochannel, + ioc); } void migration_ioc_unregister_yank(QIOChannel *ioc) { - if (migration_ioc_yank_supported(ioc)) { - yank_unregister_function(MIGRATION_YANK_INSTANCE, - migration_yank_iochannel, - ioc); + Yankable *entry, *tmp; + + if (!ioc || !migration_ioc_yank_supported(ioc)) { + return; } + + WITH_QEMU_LOCK_GUARD(&ioc_list_lock) { + QLIST_FOREACH_SAFE(entry, &yankable_ioc_list, next, tmp) { + if (entry->opaque == ioc) { + yankable_unref(entry); + + if (!entry->refcnt) { + QLIST_REMOVE(entry, next); + g_free(entry); + goto unreg; + } + } + } + } + + return; + +unreg: + yank_unregister_function(MIGRATION_YANK_INSTANCE, + migration_yank_iochannel, + ioc); + object_unref(ioc); } void migration_ioc_unregister_yank_from_file(QEMUFile *file) diff --git a/migration/yank_functions.h b/migration/yank_functions.h index a7577955ed..c928a46f68 100644 --- a/migration/yank_functions.h +++ b/migration/yank_functions.h @@ -7,6 +7,14 @@ * See the COPYING file in the top-level directory. */ +struct Yankable { + void *opaque; + int refcnt; + QLIST_ENTRY(Yankable) next; +}; + +typedef struct Yankable Yankable; + /** * migration_yank_iochannel: yank function for iochannel * -- 2.35.3