Introduce a PostcopyPageHandler callback type and use it to decouple
the userfaultfd fault thread from the postcopy-specific page request
logic. This prepares for fast snapshot load, which will supply pages
from a local file instead of requesting them over the network.
The fault thread now calls mis->page_fault_handler() instead of
postcopy_request_page() directly. For postcopy, the handler is set to
postcopy_page_fault_handler() which delegates to the existing
postcopy_request_page().
No functional change for postcopy migration.
Signed-off-by: Takeru Hayasaka <hayatake396@gmail.com>
---
migration/migration.h | 16 ++++++++++++++++
migration/postcopy-ram.c | 22 +++++++++++++++++++---
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/migration/migration.h b/migration/migration.h
index b6888daceddf..33525402922d 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -89,6 +89,17 @@ typedef enum {
PREEMPT_THREAD_QUIT,
} PreemptThreadStatus;
+/*
+ * Callback to handle a page fault from the userfaultfd fault thread.
+ * Implementations resolve the fault by supplying the requested page,
+ * e.g., by requesting it from the migration source (postcopy) or by
+ * reading it from a snapshot file (fast snapshot load).
+ */
+typedef int (*PostcopyPageHandler)(MigrationIncomingState *mis,
+ RAMBlock *rb,
+ ram_addr_t offset,
+ void *fault_address);
+
/* State for the incoming migration */
struct MigrationIncomingState {
QEMUFile *from_src_file;
@@ -116,6 +127,11 @@ struct MigrationIncomingState {
QemuThread fault_thread;
/* Set this when we want the fault thread to quit */
bool fault_thread_quit;
+ /* Callback to resolve page faults; set before fault thread starts */
+ PostcopyPageHandler page_fault_handler;
+ void *page_fault_opaque;
+ /* ptid from current uffd fault msg, for postcopy blocktime tracking */
+ uint32_t fault_thread_ptid;
bool have_listen_thread;
QemuThread listen_thread;
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index f5ef93f1933c..8dcd8ff35e85 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -976,6 +976,20 @@ static int postcopy_request_page(MigrationIncomingState *mis, RAMBlock *rb,
return migrate_send_rp_req_pages(mis, rb, start, haddr, tid);
}
+/*
+ * Default page fault handler for postcopy live migration.
+ * Requests the faulted page from the source via the return path.
+ */
+static int postcopy_page_fault_handler(MigrationIncomingState *mis,
+ RAMBlock *rb,
+ ram_addr_t offset,
+ void *fault_address)
+{
+ return postcopy_request_page(mis, rb, offset,
+ (uint64_t)(uintptr_t)fault_address,
+ mis->fault_thread_ptid);
+}
+
/*
* Callback from shared fault handlers to ask for a page,
* the page must be specified by a RAMBlock and an offset in that rb
@@ -1392,9 +1406,9 @@ retry:
* Send the request to the source - we want to request one
* of our host page sizes (which is >= TPS)
*/
- ret = postcopy_request_page(mis, rb, rb_offset,
- msg.arg.pagefault.address,
- msg.arg.pagefault.feat.ptid);
+ mis->fault_thread_ptid = msg.arg.pagefault.feat.ptid;
+ ret = mis->page_fault_handler(mis, rb, rb_offset,
+ (void *)(uintptr_t)msg.arg.pagefault.address);
if (ret) {
/* May be network failure, try to wait for recovery */
postcopy_pause_fault_thread(mis);
@@ -1552,6 +1566,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
return -1;
}
+ mis->page_fault_handler = postcopy_page_fault_handler;
+
postcopy_thread_create(mis, &mis->fault_thread,
MIGRATION_THREAD_DST_FAULT,
postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE);
--
2.43.0
© 2016 - 2026 Red Hat, Inc.