|
|
1be5c7 |
From 7a7e2191f1ac4114380248cbd3c6ab7425250747 Mon Sep 17 00:00:00 2001
|
|
|
1be5c7 |
From: Leonardo Bras <leobras@redhat.com>
|
|
|
1be5c7 |
Date: Wed, 18 May 2022 02:52:25 -0300
|
|
|
1be5c7 |
Subject: [PATCH 23/37] multifd: Implement zero copy write in multifd migration
|
|
|
1be5c7 |
(multifd-zero-copy)
|
|
|
1be5c7 |
MIME-Version: 1.0
|
|
|
1be5c7 |
Content-Type: text/plain; charset=UTF-8
|
|
|
1be5c7 |
Content-Transfer-Encoding: 8bit
|
|
|
1be5c7 |
|
|
|
1be5c7 |
RH-Author: Leonardo Brás <leobras@redhat.com>
|
|
|
1be5c7 |
RH-MergeRequest: 191: MSG_ZEROCOPY + Multifd @ rhel8.7
|
|
|
1be5c7 |
RH-Commit: [23/26] 904ce3909cfef62dd84cc7d3c6a3482e7e6f28e9
|
|
|
1be5c7 |
RH-Bugzilla: 2072049
|
|
|
1be5c7 |
RH-Acked-by: Peter Xu <peterx@redhat.com>
|
|
|
1be5c7 |
RH-Acked-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
|
1be5c7 |
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
|
1be5c7 |
|
|
|
1be5c7 |
Implement zero copy send on nocomp_send_write(), by making use of QIOChannel
|
|
|
1be5c7 |
writev + flags & flush interface.
|
|
|
1be5c7 |
|
|
|
1be5c7 |
Change multifd_send_sync_main() so flush_zero_copy() can be called
|
|
|
1be5c7 |
after each iteration in order to make sure all dirty pages are sent before
|
|
|
1be5c7 |
a new iteration is started. It will also flush at the beginning and at the
|
|
|
1be5c7 |
end of migration.
|
|
|
1be5c7 |
|
|
|
1be5c7 |
Also make it return -1 if flush_zero_copy() fails, in order to cancel
|
|
|
1be5c7 |
the migration process, and avoid resuming the guest in the target host
|
|
|
1be5c7 |
without receiving all current RAM.
|
|
|
1be5c7 |
|
|
|
1be5c7 |
This will work fine on RAM migration because the RAM pages are not usually freed,
|
|
|
1be5c7 |
and there is no problem on changing the pages content between writev_zero_copy() and
|
|
|
1be5c7 |
the actual sending of the buffer, because this change will dirty the page and
|
|
|
1be5c7 |
cause it to be re-sent on a next iteration anyway.
|
|
|
1be5c7 |
|
|
|
1be5c7 |
A lot of locked memory may be needed in order to use multifd migration
|
|
|
1be5c7 |
with zero-copy enabled, so disabling the feature should be necessary for
|
|
|
1be5c7 |
low-privileged users trying to perform multifd migrations.
|
|
|
1be5c7 |
|
|
|
1be5c7 |
Signed-off-by: Leonardo Bras <leobras@redhat.com>
|
|
|
1be5c7 |
Reviewed-by: Peter Xu <peterx@redhat.com>
|
|
|
1be5c7 |
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
|
1be5c7 |
Message-Id: <20220513062836.965425-9-leobras@redhat.com>
|
|
|
1be5c7 |
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
|
1be5c7 |
(cherry picked from commit 5b1d9bab2da4fca3a3caee97c430e5709cb32b7b)
|
|
|
1be5c7 |
Signed-off-by: Leonardo Bras <leobras@redhat.com>
|
|
|
1be5c7 |
---
|
|
|
1be5c7 |
migration/migration.c | 11 ++++++++++-
|
|
|
1be5c7 |
migration/multifd.c | 37 +++++++++++++++++++++++++++++++++++--
|
|
|
1be5c7 |
migration/multifd.h | 2 ++
|
|
|
1be5c7 |
migration/socket.c | 5 +++--
|
|
|
1be5c7 |
4 files changed, 50 insertions(+), 5 deletions(-)
|
|
|
1be5c7 |
|
|
|
1be5c7 |
diff --git a/migration/migration.c b/migration/migration.c
|
|
|
1be5c7 |
index 8e28f2ee41..5357efd348 100644
|
|
|
1be5c7 |
--- a/migration/migration.c
|
|
|
1be5c7 |
+++ b/migration/migration.c
|
|
|
1be5c7 |
@@ -1471,7 +1471,16 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp)
|
|
|
1be5c7 |
error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: ");
|
|
|
1be5c7 |
return false;
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
-
|
|
|
1be5c7 |
+#ifdef CONFIG_LINUX
|
|
|
1be5c7 |
+ if (params->zero_copy_send &&
|
|
|
1be5c7 |
+ (!migrate_use_multifd() ||
|
|
|
1be5c7 |
+ params->multifd_compression != MULTIFD_COMPRESSION_NONE ||
|
|
|
1be5c7 |
+ (params->tls_creds && *params->tls_creds))) {
|
|
|
1be5c7 |
+ error_setg(errp,
|
|
|
1be5c7 |
+ "Zero copy only available for non-compressed non-TLS multifd migration");
|
|
|
1be5c7 |
+ return false;
|
|
|
1be5c7 |
+ }
|
|
|
1be5c7 |
+#endif
|
|
|
1be5c7 |
return true;
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
|
|
|
1be5c7 |
diff --git a/migration/multifd.c b/migration/multifd.c
|
|
|
1be5c7 |
index 193f70cdba..90ab4c4346 100644
|
|
|
1be5c7 |
--- a/migration/multifd.c
|
|
|
1be5c7 |
+++ b/migration/multifd.c
|
|
|
1be5c7 |
@@ -576,6 +576,7 @@ void multifd_save_cleanup(void)
|
|
|
1be5c7 |
int multifd_send_sync_main(QEMUFile *f)
|
|
|
1be5c7 |
{
|
|
|
1be5c7 |
int i;
|
|
|
1be5c7 |
+ bool flush_zero_copy;
|
|
|
1be5c7 |
|
|
|
1be5c7 |
if (!migrate_use_multifd()) {
|
|
|
1be5c7 |
return 0;
|
|
|
1be5c7 |
@@ -586,6 +587,20 @@ int multifd_send_sync_main(QEMUFile *f)
|
|
|
1be5c7 |
return -1;
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
+ /*
|
|
|
1be5c7 |
+ * When using zero-copy, it's necessary to flush the pages before any of
|
|
|
1be5c7 |
+ * the pages can be sent again, so we'll make sure the new version of the
|
|
|
1be5c7 |
+ * pages will always arrive _later_ than the old pages.
|
|
|
1be5c7 |
+ *
|
|
|
1be5c7 |
+ * Currently we achieve this by flushing the zero-page requested writes
|
|
|
1be5c7 |
+ * per ram iteration, but in the future we could potentially optimize it
|
|
|
1be5c7 |
+ * to be less frequent, e.g. only after we finished one whole scanning of
|
|
|
1be5c7 |
+ * all the dirty bitmaps.
|
|
|
1be5c7 |
+ */
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
+ flush_zero_copy = migrate_use_zero_copy_send();
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
for (i = 0; i < migrate_multifd_channels(); i++) {
|
|
|
1be5c7 |
MultiFDSendParams *p = &multifd_send_state->params[i];
|
|
|
1be5c7 |
|
|
|
1be5c7 |
@@ -607,6 +622,17 @@ int multifd_send_sync_main(QEMUFile *f)
|
|
|
1be5c7 |
ram_counters.transferred += p->packet_len;
|
|
|
1be5c7 |
qemu_mutex_unlock(&p->mutex);
|
|
|
1be5c7 |
qemu_sem_post(&p->sem);
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
+ if (flush_zero_copy && p->c) {
|
|
|
1be5c7 |
+ int ret;
|
|
|
1be5c7 |
+ Error *err = NULL;
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
+ ret = qio_channel_flush(p->c, &err;;
|
|
|
1be5c7 |
+ if (ret < 0) {
|
|
|
1be5c7 |
+ error_report_err(err);
|
|
|
1be5c7 |
+ return -1;
|
|
|
1be5c7 |
+ }
|
|
|
1be5c7 |
+ }
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
for (i = 0; i < migrate_multifd_channels(); i++) {
|
|
|
1be5c7 |
MultiFDSendParams *p = &multifd_send_state->params[i];
|
|
|
1be5c7 |
@@ -691,8 +717,8 @@ static void *multifd_send_thread(void *opaque)
|
|
|
1be5c7 |
p->iov[0].iov_base = p->packet;
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
|
|
|
1be5c7 |
- ret = qio_channel_writev_all(p->c, p->iov, p->iovs_num,
|
|
|
1be5c7 |
- &local_err);
|
|
|
1be5c7 |
+ ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, NULL,
|
|
|
1be5c7 |
+ 0, p->write_flags, &local_err);
|
|
|
1be5c7 |
if (ret != 0) {
|
|
|
1be5c7 |
break;
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
@@ -933,6 +959,13 @@ int multifd_save_setup(Error **errp)
|
|
|
1be5c7 |
/* We need one extra place for the packet header */
|
|
|
1be5c7 |
p->iov = g_new0(struct iovec, page_count + 1);
|
|
|
1be5c7 |
p->normal = g_new0(ram_addr_t, page_count);
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
+ if (migrate_use_zero_copy_send()) {
|
|
|
1be5c7 |
+ p->write_flags = QIO_CHANNEL_WRITE_FLAG_ZERO_COPY;
|
|
|
1be5c7 |
+ } else {
|
|
|
1be5c7 |
+ p->write_flags = 0;
|
|
|
1be5c7 |
+ }
|
|
|
1be5c7 |
+
|
|
|
1be5c7 |
socket_send_channel_create(multifd_new_send_channel_async, p);
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
|
|
|
1be5c7 |
diff --git a/migration/multifd.h b/migration/multifd.h
|
|
|
1be5c7 |
index 92de878155..11d5e273e6 100644
|
|
|
1be5c7 |
--- a/migration/multifd.h
|
|
|
1be5c7 |
+++ b/migration/multifd.h
|
|
|
1be5c7 |
@@ -95,6 +95,8 @@ typedef struct {
|
|
|
1be5c7 |
uint32_t packet_len;
|
|
|
1be5c7 |
/* pointer to the packet */
|
|
|
1be5c7 |
MultiFDPacket_t *packet;
|
|
|
1be5c7 |
+ /* multifd flags for sending ram */
|
|
|
1be5c7 |
+ int write_flags;
|
|
|
1be5c7 |
/* multifd flags for each packet */
|
|
|
1be5c7 |
uint32_t flags;
|
|
|
1be5c7 |
/* size of the next packet that contains pages */
|
|
|
1be5c7 |
diff --git a/migration/socket.c b/migration/socket.c
|
|
|
1be5c7 |
index 3754d8f72c..4fd5e85f50 100644
|
|
|
1be5c7 |
--- a/migration/socket.c
|
|
|
1be5c7 |
+++ b/migration/socket.c
|
|
|
1be5c7 |
@@ -79,8 +79,9 @@ static void socket_outgoing_migration(QIOTask *task,
|
|
|
1be5c7 |
|
|
|
1be5c7 |
trace_migration_socket_outgoing_connected(data->hostname);
|
|
|
1be5c7 |
|
|
|
1be5c7 |
- if (migrate_use_zero_copy_send()) {
|
|
|
1be5c7 |
- error_setg(&err, "Zero copy send not available in migration");
|
|
|
1be5c7 |
+ if (migrate_use_zero_copy_send() &&
|
|
|
1be5c7 |
+ !qio_channel_has_feature(sioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) {
|
|
|
1be5c7 |
+ error_setg(&err, "Zero copy send feature not detected in host kernel");
|
|
|
1be5c7 |
}
|
|
|
1be5c7 |
|
|
|
1be5c7 |
out:
|
|
|
1be5c7 |
--
|
|
|
1be5c7 |
2.35.3
|
|
|
1be5c7 |
|