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));