Commit 81344085d5 for qemu.org

commit 81344085d50d77ca3a9dfaabf13d9fa33c694038
Author: Yanfei Xu <yanfei.xu@bytedance.com>
Date:   Sun Sep 13 14:24:53 2026 +0800

    migration/rdma: Drain writes at RAM round boundaries

    rdma_registration_stop() runs after every ram_save_iterate() call, which
    can stop before a complete RAM scan because of rate limiting.  Draining
    there ties the completion barrier to a SaveVM section rather than the
    point where the scan wraps and a newer page version can be sent.

    Register an RDMA RAM round notifier that drains all outstanding writes.
    Keep posting the buffered tail in rdma_registration_stop() so it remains
    ordered before REGISTER_FINISHED.

    Signed-off-by: Yanfei Xu <yanfei.xu@bytedance.com>
    Reviewed-by: Peter Xu <peterx@redhat.com>
    Tested-by: Jack Wang <jinpu.wang@cloud.ionos.com>
    Signed-off-by: Fabiano Rosas <farosas@suse.de>

diff --git a/migration/rdma.c b/migration/rdma.c
index 35d918ec83..7d220acef8 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -2636,21 +2636,16 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
     return done;
 }

-/*
- * Block until all the outstanding chunks have been delivered by the hardware.
- */
-static int qemu_rdma_drain_cq(RDMAContext *rdma)
+/* Block until all outstanding writes have been delivered by the hardware. */
+static int qemu_rdma_drain_cq(RDMAContext *rdma, Error **errp)
 {
-    Error *err = NULL;
-
-    if (qemu_rdma_write_flush(rdma, &err) < 0) {
-        error_report_err(err);
+    if (qemu_rdma_write_flush(rdma, errp) < 0) {
         return -1;
     }

     while (rdma->nb_sent) {
         if (qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL) < 0) {
-            error_report("rdma migration: complete polling error!");
+            error_setg(errp, "rdma migration: completion polling error");
             return -1;
         }
     }
@@ -2658,6 +2653,38 @@ static int qemu_rdma_drain_cq(RDMAContext *rdma)
     return 0;
 }

+static int rdma_ram_round_notify(NotifierWithReturn *n G_GNUC_UNUSED,
+                                 void *data, Error **errp)
+{
+    RAMRoundNotifyData *round_data = data;
+    QEMUFile *f;
+    QIOChannelRDMA *rioc;
+    RDMAContext *rdma;
+
+    if (!migrate_rdma()) {
+        return 0;
+    }
+
+    f = round_data->file;
+    RCU_READ_LOCK_GUARD();
+    rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f));
+    rdma = qatomic_rcu_read(&rioc->rdmaout);
+    if (!rdma) {
+        error_setg(errp, "RDMA output context is not set");
+        return -1;
+    }
+
+    if (rdma_errored(rdma)) {
+        error_setg(errp, "RDMA is in an error state");
+        return -1;
+    }
+
+    return qemu_rdma_drain_cq(rdma, errp);
+}
+
+static NotifierWithReturn rdma_ram_round_notifier = {
+    .notify = rdma_ram_round_notify,
+};

 static int qio_channel_rdma_set_blocking(QIOChannel *ioc,
                                          bool blocking,
@@ -3558,7 +3585,8 @@ int rdma_registration_start(QEMUFile *f, uint64_t flags)

 /*
  * Inform dest that dynamic registrations are done for now.
- * First, flush writes, if any.
+ * Post any buffered write before sending the control marker. Outstanding
+ * writes are drained at the RAM scan round boundary.
  */
 int rdma_registration_stop(QEMUFile *f, uint64_t flags)
 {
@@ -3584,9 +3612,10 @@ int rdma_registration_stop(QEMUFile *f, uint64_t flags)
     }

     qemu_fflush(f);
-    ret = qemu_rdma_drain_cq(rdma);
+    ret = qemu_rdma_write_flush(rdma, &err);

     if (ret < 0) {
+        error_report_err(err);
         goto err;
     }

@@ -3720,6 +3749,13 @@ static void qio_channel_rdma_register_types(void)

 type_init(qio_channel_rdma_register_types);

+static void rdma_register_migration_notifiers(void)
+{
+    ram_round_add_notifier(&rdma_ram_round_notifier);
+}
+
+migration_init(rdma_register_migration_notifiers);
+
 static QIOChannel *rdma_new_input(RDMAContext *rdma)
 {
     QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA));