summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--exec.c65
-rw-r--r--include/exec/memory-internal.h62
2 files changed, 106 insertions, 21 deletions
diff --git a/exec.c b/exec.c
index 2202f2d731..03238a3449 100644
--- a/exec.c
+++ b/exec.c
@@ -2354,18 +2354,55 @@ ram_addr_t qemu_ram_addr_from_host(void *ptr)
     return block->offset + offset;
 }
 
-/* Called within RCU critical section.  */
-static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
-                               uint64_t val, unsigned size)
-{
-    bool locked = false;
+/* Called within RCU critical section. */
+void memory_notdirty_write_prepare(NotDirtyInfo *ndi,
+                          CPUState *cpu,
+                          vaddr mem_vaddr,
+                          ram_addr_t ram_addr,
+                          unsigned size)
+{
+    ndi->cpu = cpu;
+    ndi->ram_addr = ram_addr;
+    ndi->mem_vaddr = mem_vaddr;
+    ndi->size = size;
+    ndi->locked = false;
 
     assert(tcg_enabled());
     if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
-        locked = true;
+        ndi->locked = true;
         tb_lock();
         tb_invalidate_phys_page_fast(ram_addr, size);
     }
+}
+
+/* Called within RCU critical section. */
+void memory_notdirty_write_complete(NotDirtyInfo *ndi)
+{
+    if (ndi->locked) {
+        tb_unlock();
+    }
+
+    /* Set both VGA and migration bits for simplicity and to remove
+     * the notdirty callback faster.
+     */
+    cpu_physical_memory_set_dirty_range(ndi->ram_addr, ndi->size,
+                                        DIRTY_CLIENTS_NOCODE);
+    /* we remove the notdirty callback only if the code has been
+       flushed */
+    if (!cpu_physical_memory_is_clean(ndi->ram_addr)) {
+        tlb_set_dirty(ndi->cpu, ndi->mem_vaddr);
+    }
+}
+
+/* Called within RCU critical section.  */
+static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
+                               uint64_t val, unsigned size)
+{
+    NotDirtyInfo ndi;
+
+    memory_notdirty_write_prepare(&ndi, current_cpu, current_cpu->mem_io_vaddr,
+                         ram_addr, size);
+
     switch (size) {
     case 1:
         stb_p(qemu_map_ram_ptr(NULL, ram_addr), val);
@@ -2382,21 +2419,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
     default:
         abort();
     }
-
-    if (locked) {
-        tb_unlock();
-    }
-
-    /* Set both VGA and migration bits for simplicity and to remove
-     * the notdirty callback faster.
-     */
-    cpu_physical_memory_set_dirty_range(ram_addr, size,
-                                        DIRTY_CLIENTS_NOCODE);
-    /* we remove the notdirty callback only if the code has been
-       flushed */
-    if (!cpu_physical_memory_is_clean(ram_addr)) {
-        tlb_set_dirty(current_cpu, current_cpu->mem_io_vaddr);
-    }
+    memory_notdirty_write_complete(&ndi);
 }
 
 static bool notdirty_mem_accepts(void *opaque, hwaddr addr,
diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h
index 647e9bd5c4..98d82964cc 100644
--- a/include/exec/memory-internal.h
+++ b/include/exec/memory-internal.h
@@ -39,5 +39,67 @@ void mtree_print_dispatch(fprintf_function mon, void *f,
                           struct AddressSpaceDispatch *d,
                           MemoryRegion *root);
 
+/* Opaque struct for passing info from memory_notdirty_write_prepare()
+ * to memory_notdirty_write_complete(). Callers should treat all fields
+ * as private, with the exception of @active.
+ *
+ * @active is a field which is not touched by either the prepare or
+ * complete functions, but which the caller can use if it wishes to
+ * track whether it has called prepare for this struct and so needs
+ * to later call the complete function.
+ */
+typedef struct {
+    CPUState *cpu;
+    ram_addr_t ram_addr;
+    vaddr mem_vaddr;
+    unsigned size;
+    bool locked;
+    bool active;
+} NotDirtyInfo;
+
+/**
+ * memory_notdirty_write_prepare: call before writing to non-dirty memory
+ * @ndi: pointer to opaque NotDirtyInfo struct
+ * @cpu: CPU doing the write
+ * @mem_vaddr: virtual address of write
+ * @ram_addr: the ram address of the write
+ * @size: size of write in bytes
+ *
+ * Any code which writes to the host memory corresponding to
+ * guest RAM which has been marked as NOTDIRTY must wrap those
+ * writes in calls to memory_notdirty_write_prepare() and
+ * memory_notdirty_write_complete():
+ *
+ *  NotDirtyInfo ndi;
+ *  memory_notdirty_write_prepare(&ndi, ....);
+ *  ... perform write here ...
+ *  memory_notdirty_write_complete(&ndi);
+ *
+ * These calls will ensure that we flush any TCG translated code for
+ * the memory being written, update the dirty bits and (if possible)
+ * remove the slowpath callback for writing to the memory.
+ *
+ * This must only be called if we are using TCG; it will assert otherwise.
+ *
+ * We may take a lock in the prepare call, so callers must ensure that
+ * they don't exit (via longjump or otherwise) without calling complete.
+ *
+ * This call must only be made inside an RCU critical section.
+ * (Note that while we're executing a TCG TB we're always in an
+ * RCU critical section, which is likely to be the case for callers
+ * of these functions.)
+ */
+void memory_notdirty_write_prepare(NotDirtyInfo *ndi,
+                                   CPUState *cpu,
+                                   vaddr mem_vaddr,
+                                   ram_addr_t ram_addr,
+                                   unsigned size);
+/**
+ * memory_notdirty_write_complete: finish write to non-dirty memory
+ * @ndi: pointer to the opaque NotDirtyInfo struct which was initialized
+ * by memory_not_dirty_write_prepare().
+ */
+void memory_notdirty_write_complete(NotDirtyInfo *ndi);
+
 #endif
 #endif