It's the only function making a memory page copy.
It supports multithreading semantics ensuring that
the page is copied by one thread only and releasing
the copied page from write protection.
Signed-off-by: Denis Plotnikov <dplotnikov@virtuozzo.com>
---
migration/ram.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
migration/ram.h | 1 +
2 files changed, 57 insertions(+)
diff --git a/migration/ram.c b/migration/ram.c
index 3c4ccd85b4..10b6fdf23e 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1706,6 +1706,62 @@ RAMBlock *ram_bgs_block_find(uint8_t *address, ram_addr_t *page_offset)
return NULL;
}
+/**
+ * ram_copy_page: make a page copy
+ *
+ * Used in the background snapshot to make a copy of a memeory page.
+ * Ensures that the memeory page is copied only once.
+ * When a page copy is done, restores read/write access to the memory
+ * page.
+ * If a page is being copied by another thread, wait until the copying
+ * ends and exit.
+ *
+ * Returns:
+ * -1 - on error
+ * 0 - the page wasn't copied by the function call
+ * 1 - the page has been copied
+ *
+ * @block: RAM block to use
+ * @page_nr: the page number to copy
+ * @page_copy: the pointer to return a page copy
+ *
+ */
+int ram_copy_page(RAMBlock *block, unsigned long page_nr,
+ void **page_copy)
+{
+ void *host_page;
+
+ if (test_and_set_bit_atomic(page_nr, block->touched_map)) {
+ while (!test_bit_atomic(page_nr, block->copied_map)) {
+ /* the page is being copied -- wait for the end of the copying
+ * and check once again
+ */
+ qemu_event_wait(&ram_state->page_copying_done);
+ }
+ return 0;
+ }
+
+ *page_copy = ram_page_buffer_get();
+ if (!*page_copy) {
+ return -1;
+ }
+
+ host_page = block->host + (page_nr << TARGET_PAGE_BITS);
+ memcpy(*page_copy, host_page, TARGET_PAGE_SIZE);
+
+ if (ram_set_rw(host_page, TARGET_PAGE_SIZE)) {
+ ram_page_buffer_free(*page_copy);
+ *page_copy = NULL;
+ return -1;
+ }
+
+ set_bit_atomic(page_nr, block->copied_map);
+ qemu_event_set(&ram_state->page_copying_done);
+ qemu_event_reset(&ram_state->page_copying_done);
+
+ return 1;
+}
+
/**
* ram_find_and_save_block: finds a dirty page and sends it to f
*
diff --git a/migration/ram.h b/migration/ram.h
index c3679ba65e..76ab0a3377 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -75,4 +75,5 @@ int ram_page_buffer_free(void *buffer);
int ram_block_list_set_readonly(void);
int ram_block_list_set_writable(void);
+int ram_copy_page(RAMBlock *block, unsigned long page_nr, void **page_copy);
#endif
--
2.17.0