]>
Commit | Line | Data |
---|---|---|
3dcc8d3b | 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
ddbcf45e WB |
2 | From: Gerd Hoffmann <kraxel@redhat.com> |
3 | Date: Fri, 21 Apr 2017 11:16:25 +0200 | |
3dcc8d3b | 4 | Subject: [PATCH] memory: add support getting and using a dirty bitmap copy. |
ddbcf45e WB |
5 | |
6 | This patch adds support for getting and using a local copy of the dirty | |
7 | bitmap. | |
8 | ||
9 | memory_region_snapshot_and_clear_dirty() will create a snapshot of the | |
10 | dirty bitmap for the specified range, clear the dirty bitmap and return | |
11 | the copy. The returned bitmap can be a bit larger than requested, the | |
12 | range is expanded so the code can copy unsigned longs from the bitmap | |
13 | and avoid atomic bit update operations. | |
14 | ||
15 | memory_region_snapshot_get_dirty() will return the dirty status of | |
16 | pages, pretty much like memory_region_get_dirty(), but using the copy | |
17 | returned by memory_region_copy_and_clear_dirty(). | |
18 | ||
19 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | |
20 | Message-id: 20170421091632.30900-3-kraxel@redhat.com | |
21 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | |
22 | --- | |
23 | exec.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
24 | include/exec/memory.h | 47 +++++++++++++++++++++++++++++++ | |
25 | include/exec/ram_addr.h | 7 +++++ | |
26 | include/qemu/typedefs.h | 1 + | |
27 | memory.c | 17 +++++++++++ | |
28 | 5 files changed, 147 insertions(+) | |
29 | ||
30 | diff --git a/exec.c b/exec.c | |
31 | index fcb5b16131..07c2c8ea88 100644 | |
32 | --- a/exec.c | |
33 | +++ b/exec.c | |
34 | @@ -223,6 +223,12 @@ struct CPUAddressSpace { | |
35 | MemoryListener tcg_as_listener; | |
36 | }; | |
37 | ||
38 | +struct DirtyBitmapSnapshot { | |
39 | + ram_addr_t start; | |
40 | + ram_addr_t end; | |
41 | + unsigned long dirty[]; | |
42 | +}; | |
43 | + | |
44 | #endif | |
45 | ||
46 | #if !defined(CONFIG_USER_ONLY) | |
47 | @@ -1061,6 +1067,75 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, | |
48 | return dirty; | |
49 | } | |
50 | ||
51 | +DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty | |
52 | + (ram_addr_t start, ram_addr_t length, unsigned client) | |
53 | +{ | |
54 | + DirtyMemoryBlocks *blocks; | |
55 | + unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL); | |
56 | + ram_addr_t first = QEMU_ALIGN_DOWN(start, align); | |
57 | + ram_addr_t last = QEMU_ALIGN_UP(start + length, align); | |
58 | + DirtyBitmapSnapshot *snap; | |
59 | + unsigned long page, end, dest; | |
60 | + | |
61 | + snap = g_malloc0(sizeof(*snap) + | |
62 | + ((last - first) >> (TARGET_PAGE_BITS + 3))); | |
63 | + snap->start = first; | |
64 | + snap->end = last; | |
65 | + | |
66 | + page = first >> TARGET_PAGE_BITS; | |
67 | + end = last >> TARGET_PAGE_BITS; | |
68 | + dest = 0; | |
69 | + | |
70 | + rcu_read_lock(); | |
71 | + | |
72 | + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); | |
73 | + | |
74 | + while (page < end) { | |
75 | + unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; | |
76 | + unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; | |
77 | + unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset); | |
78 | + | |
79 | + assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); | |
80 | + assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); | |
81 | + offset >>= BITS_PER_LEVEL; | |
82 | + | |
83 | + bitmap_copy_and_clear_atomic(snap->dirty + dest, | |
84 | + blocks->blocks[idx] + offset, | |
85 | + num); | |
86 | + page += num; | |
87 | + dest += num >> BITS_PER_LEVEL; | |
88 | + } | |
89 | + | |
90 | + rcu_read_unlock(); | |
91 | + | |
92 | + if (tcg_enabled()) { | |
93 | + tlb_reset_dirty_range_all(start, length); | |
94 | + } | |
95 | + | |
96 | + return snap; | |
97 | +} | |
98 | + | |
99 | +bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, | |
100 | + ram_addr_t start, | |
101 | + ram_addr_t length) | |
102 | +{ | |
103 | + unsigned long page, end; | |
104 | + | |
105 | + assert(start >= snap->start); | |
106 | + assert(start + length <= snap->end); | |
107 | + | |
108 | + end = TARGET_PAGE_ALIGN(start + length - snap->start) >> TARGET_PAGE_BITS; | |
109 | + page = (start - snap->start) >> TARGET_PAGE_BITS; | |
110 | + | |
111 | + while (page < end) { | |
112 | + if (test_bit(page, snap->dirty)) { | |
113 | + return true; | |
114 | + } | |
115 | + page++; | |
116 | + } | |
117 | + return false; | |
118 | +} | |
119 | + | |
120 | /* Called from RCU critical section */ | |
121 | hwaddr memory_region_section_get_iotlb(CPUState *cpu, | |
122 | MemoryRegionSection *section, | |
123 | diff --git a/include/exec/memory.h b/include/exec/memory.h | |
124 | index f20b191793..1e15e79d00 100644 | |
125 | --- a/include/exec/memory.h | |
126 | +++ b/include/exec/memory.h | |
127 | @@ -871,6 +871,53 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, | |
128 | */ | |
129 | bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, | |
130 | hwaddr size, unsigned client); | |
131 | + | |
132 | +/** | |
133 | + * memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty | |
134 | + * bitmap and clear it. | |
135 | + * | |
136 | + * Creates a snapshot of the dirty bitmap, clears the dirty bitmap and | |
137 | + * returns the snapshot. The snapshot can then be used to query dirty | |
138 | + * status, using memory_region_snapshot_get_dirty. Unlike | |
139 | + * memory_region_test_and_clear_dirty this allows to query the same | |
140 | + * page multiple times, which is especially useful for display updates | |
141 | + * where the scanlines often are not page aligned. | |
142 | + * | |
143 | + * The dirty bitmap region which gets copyed into the snapshot (and | |
144 | + * cleared afterwards) can be larger than requested. The boundaries | |
145 | + * are rounded up/down so complete bitmap longs (covering 64 pages on | |
146 | + * 64bit hosts) can be copied over into the bitmap snapshot. Which | |
147 | + * isn't a problem for display updates as the extra pages are outside | |
148 | + * the visible area, and in case the visible area changes a full | |
149 | + * display redraw is due anyway. Should other use cases for this | |
150 | + * function emerge we might have to revisit this implementation | |
151 | + * detail. | |
152 | + * | |
153 | + * Use g_free to release DirtyBitmapSnapshot. | |
154 | + * | |
155 | + * @mr: the memory region being queried. | |
156 | + * @addr: the address (relative to the start of the region) being queried. | |
157 | + * @size: the size of the range being queried. | |
158 | + * @client: the user of the logging information; typically %DIRTY_MEMORY_VGA. | |
159 | + */ | |
160 | +DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, | |
161 | + hwaddr addr, | |
162 | + hwaddr size, | |
163 | + unsigned client); | |
164 | + | |
165 | +/** | |
166 | + * memory_region_snapshot_get_dirty: Check whether a range of bytes is dirty | |
167 | + * in the specified dirty bitmap snapshot. | |
168 | + * | |
169 | + * @mr: the memory region being queried. | |
170 | + * @snap: the dirty bitmap snapshot | |
171 | + * @addr: the address (relative to the start of the region) being queried. | |
172 | + * @size: the size of the range being queried. | |
173 | + */ | |
174 | +bool memory_region_snapshot_get_dirty(MemoryRegion *mr, | |
175 | + DirtyBitmapSnapshot *snap, | |
176 | + hwaddr addr, hwaddr size); | |
177 | + | |
178 | /** | |
179 | * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with | |
180 | * any external TLBs (e.g. kvm) | |
181 | diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h | |
182 | index b05dc84ab9..2b63d7f59e 100644 | |
183 | --- a/include/exec/ram_addr.h | |
184 | +++ b/include/exec/ram_addr.h | |
185 | @@ -343,6 +343,13 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, | |
186 | ram_addr_t length, | |
187 | unsigned client); | |
188 | ||
189 | +DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty | |
190 | + (ram_addr_t start, ram_addr_t length, unsigned client); | |
191 | + | |
192 | +bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, | |
193 | + ram_addr_t start, | |
194 | + ram_addr_t length); | |
195 | + | |
196 | static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, | |
197 | ram_addr_t length) | |
198 | { | |
199 | diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h | |
200 | index e95f28cfec..f08d327aec 100644 | |
201 | --- a/include/qemu/typedefs.h | |
202 | +++ b/include/qemu/typedefs.h | |
203 | @@ -23,6 +23,7 @@ typedef struct CPUAddressSpace CPUAddressSpace; | |
204 | typedef struct CPUState CPUState; | |
205 | typedef struct DeviceListener DeviceListener; | |
206 | typedef struct DeviceState DeviceState; | |
207 | +typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; | |
208 | typedef struct DisplayChangeListener DisplayChangeListener; | |
209 | typedef struct DisplayState DisplayState; | |
210 | typedef struct DisplaySurface DisplaySurface; | |
211 | diff --git a/memory.c b/memory.c | |
212 | index 4c95aaf39c..8a0648551f 100644 | |
213 | --- a/memory.c | |
214 | +++ b/memory.c | |
215 | @@ -1716,6 +1716,23 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, | |
216 | memory_region_get_ram_addr(mr) + addr, size, client); | |
217 | } | |
218 | ||
219 | +DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, | |
220 | + hwaddr addr, | |
221 | + hwaddr size, | |
222 | + unsigned client) | |
223 | +{ | |
224 | + assert(mr->ram_block); | |
225 | + return cpu_physical_memory_snapshot_and_clear_dirty( | |
226 | + memory_region_get_ram_addr(mr) + addr, size, client); | |
227 | +} | |
228 | + | |
229 | +bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap, | |
230 | + hwaddr addr, hwaddr size) | |
231 | +{ | |
232 | + assert(mr->ram_block); | |
233 | + return cpu_physical_memory_snapshot_get_dirty(snap, | |
234 | + memory_region_get_ram_addr(mr) + addr, size); | |
235 | +} | |
236 | ||
237 | void memory_region_sync_dirty_bitmap(MemoryRegion *mr) | |
238 | { | |
239 | -- | |
240 | 2.11.0 | |
241 |