All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/1] migration/cpr: lock the cpr state
@ 2026-09-16 13:24 Denis V. Lunev
  2026-09-16 18:25 ` Fabiano Rosas
  2026-09-16 18:34 ` Peter Xu
  0 siblings, 2 replies; 15+ messages in thread
From: Denis V. Lunev @ 2026-09-16 13:24 UTC (permalink / raw)
  To: qemu-devel
  Cc: den, Maciej S. Szmigiero, Peter Xu, Fabiano Rosas, Mark Kanda,
	Ben Chaney, Steve Sistare

From: Denis V. Lunev <den@openvz.org>

The cpr state is a process wide registry of the resources that have to
survive an exec. Entries are added and dropped from realize and teardown
paths all over the tree, and a caller has no way to tell which thread the
next caller will run in.

The state carries no serialization of its own. It is safe today only
because its callers happen to hold the BQL, and that is a property of
where those callers live rather than anything the interface promises or
checks. A caller that is correct within its own subsystem can therefore
break this one from a distance: two that overlap corrupt the registry,
and the next lookup walks an entry that has already been freed.

Give the state a lock of its own instead of relying on that. Entries
move when a device is realized, when a resource is handed over, and when
a migration starts, so none of this is on a hot path and an uncontended
mutex costs nothing.

Take the lock in every accessor and across the save and load walks.
Holding it over the walk callbacks is safe because none of them comes
back through this interface. State that is not reached through these
accessors, such as the VFIO device list that hw/vfio manipulates
directly, stays outside it.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Cc: Maciej S. Szmigiero <maciej.szmigiero@oracle.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Fabiano Rosas <farosas@suse.de>
Cc: Mark Kanda <mark.kanda@oracle.com>
Cc: Ben Chaney <bchaney@akamai.com>
Cc: Steve Sistare <steven.sistare@oracle.com>
---
 migration/cpr.c | 59 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 49 insertions(+), 10 deletions(-)

diff --git a/migration/cpr.c b/migration/cpr.c
index bca43e9bf3..503057e262 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -9,6 +9,7 @@
 #include "qemu/error-report.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
+#include "qemu/lockable.h"
 #include "hw/vfio/vfio-cpr.h"
 #include "migration/cpr.h"
 #include "migration/misc.h"
@@ -26,6 +27,14 @@
 CprState cpr_state;
 static GHashTable *cpr_fds_hash;
 
+/* Callers are spread across the tree and need not share a thread. */
+static QemuMutex cpr_fds_lock;
+
+static void __attribute__((constructor)) cpr_fds_lock_init(void)
+{
+    qemu_mutex_init(&cpr_fds_lock);
+}
+
 /****************************************************************************/
 
 typedef struct CprFd {
@@ -94,6 +103,8 @@ static int cpr_fd_pre_save(void *opaque)
     GHashTableIter iter;
     CprFd *elem;
 
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
     QLIST_INIT(&state->fds);
 
     g_hash_table_iter_init(&iter, get_cpr_fds_hash());
@@ -109,6 +120,8 @@ static int cpr_fd_post_load(void *opaque, int version_id)
     CprState *state = (CprState *)opaque;
     CprFd *elem;
 
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
     while ((elem = QLIST_FIRST(&state->fds))) {
         QLIST_REMOVE(elem, next);
 
@@ -127,7 +140,7 @@ static int cpr_fd_post_load(void *opaque, int version_id)
     return 0;
 }
 
-void cpr_save_fd(const char *name, int id, int fd)
+static void do_cpr_save_fd(const char *name, int id, int fd)
 {
     CprFd *elem = g_new0(CprFd, 1);
 
@@ -139,6 +152,13 @@ void cpr_save_fd(const char *name, int id, int fd)
     cpr_fd_hash_insert(elem);
 }
 
+void cpr_save_fd(const char *name, int id, int fd)
+{
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
+    do_cpr_save_fd(name, id, fd);
+}
+
 static CprFd *find_fd(const char *name, int id)
 {
     CprFd key = {
@@ -149,6 +169,15 @@ static CprFd *find_fd(const char *name, int id)
     return g_hash_table_lookup(get_cpr_fds_hash(), &key);
 }
 
+static int do_cpr_find_fd(const char *name, int id)
+{
+    CprFd *elem = find_fd(name, id);
+    int fd = elem ? elem->fd : -1;
+
+    trace_cpr_find_fd(name, id, fd);
+    return fd;
+}
+
 void cpr_delete_fd(const char *name, int id)
 {
     CprFd key = {
@@ -156,6 +185,8 @@ void cpr_delete_fd(const char *name, int id)
         .id = id,
     };
 
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
     g_hash_table_remove(get_cpr_fds_hash(), &key);
 
     trace_cpr_delete_fd(name, id);
@@ -163,20 +194,23 @@ void cpr_delete_fd(const char *name, int id)
 
 int cpr_find_fd(const char *name, int id)
 {
-    CprFd *elem = find_fd(name, id);
-    int fd = elem ? elem->fd : -1;
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
 
-    trace_cpr_find_fd(name, id, fd);
-    return fd;
+    return do_cpr_find_fd(name, id);
 }
 
 void cpr_resave_fd(const char *name, int id, int fd)
 {
-    CprFd *elem = find_fd(name, id);
-    int old_fd = elem ? elem->fd : -1;
+    CprFd *elem;
+    int old_fd;
+
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
+    elem = find_fd(name, id);
+    old_fd = elem ? elem->fd : -1;
 
     if (old_fd < 0) {
-        cpr_save_fd(name, id, fd);
+        do_cpr_save_fd(name, id, fd);
     } else if (old_fd != fd) {
         error_report("internal error: cpr fd '%s' id %d value %d "
                      "already saved with a different value %d",
@@ -188,12 +222,15 @@ void cpr_resave_fd(const char *name, int id, int fd)
 int cpr_open_fd(const char *path, int flags, const char *name, int id,
                 Error **errp)
 {
-    int fd = cpr_find_fd(name, id);
+    int fd;
 
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
+    fd = do_cpr_find_fd(name, id);
     if (fd < 0) {
         fd = qemu_open(path, flags, errp);
         if (fd >= 0) {
-            cpr_save_fd(name, id, fd);
+            do_cpr_save_fd(name, id, fd);
         }
     }
     return fd;
@@ -204,6 +241,8 @@ bool cpr_walk_fd(cpr_walk_fd_cb cb)
     GHashTableIter iter;
     CprFd *elem;
 
+    QEMU_LOCK_GUARD(&cpr_fds_lock);
+
     g_hash_table_iter_init(&iter, get_cpr_fds_hash());
     while (g_hash_table_iter_next(&iter, (gpointer *)&elem, NULL)) {
         g_assert(elem->fd >= 0);
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-09-17 14:50 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16 13:24 [PATCH 1/1] migration/cpr: lock the cpr state Denis V. Lunev
2026-09-16 18:25 ` Fabiano Rosas
2026-09-16 18:34 ` Peter Xu
2026-09-16 18:57   ` Fabiano Rosas
2026-09-16 19:37     ` Peter Xu
2026-09-17 12:24       ` Maciej S. Szmigiero
2026-09-17 12:36         ` Peter Xu
2026-09-17 12:44           ` Peter Xu
2026-09-17 14:17             ` Maciej S. Szmigiero
2026-09-17 14:37               ` Peter Xu
2026-09-17 14:45                 ` Denis V. Lunev
2026-09-16 19:45     ` Denis V. Lunev
2026-09-16 20:43       ` Peter Xu
2026-09-17  9:48         ` Denis V. Lunev
2026-09-17 12:29           ` Peter Xu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.