ram_addr_t start,
ram_addr_t length)
{
- ram_addr_t addr;
unsigned long word = BIT_WORD((start + rb->offset) >> TARGET_PAGE_BITS);
uint64_t num_dirty = 0;
unsigned long *dest = rb->bmap;
memory_region_clear_dirty_bitmap(rb->mr, start, length);
}
} else {
- ram_addr_t offset = rb->offset;
-
- for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) {
- if (physical_memory_test_and_clear_dirty(
- start + addr + offset,
- TARGET_PAGE_SIZE,
- DIRTY_MEMORY_MIGRATION)) {
- long k = (start + addr) >> TARGET_PAGE_BITS;
- if (!test_and_set_bit(k, dest)) {
- num_dirty++;
- }
- }
- }
+ num_dirty = physical_memory_test_and_clear_dirty(
+ start + rb->offset,
+ length,
+ DIRTY_MEMORY_MIGRATION,
+ dest);
}
return num_dirty;
}
}
-/* Note: start and end must be within the same ram block. */
-bool physical_memory_test_and_clear_dirty(ram_addr_t start,
+/*
+ * Note: start and end must be within the same ram block.
+ *
+ * @bmap usage:
+ * - When @bmap is provided, set bits for dirty pages, but
+ * only count those pages if the bit wasn't already set in @bmap.
+ * - When @bmap is NULL, count all dirty pages in the range.
+ *
+ * @return:
+ * - Number of dirty guest pages found within [start, start + length).
+ */
+uint64_t physical_memory_test_and_clear_dirty(ram_addr_t start,
ram_addr_t length,
- unsigned client)
+ unsigned client,
+ unsigned long *bmap)
{
DirtyMemoryBlocks *blocks;
unsigned long end, page, start_page;
- bool dirty = false;
+ uint64_t num_dirty = 0;
RAMBlock *ramblock;
uint64_t mr_offset, mr_size;
if (length == 0) {
- return false;
+ return 0;
}
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
while (page < end) {
unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long num = MIN(end - page,
- DIRTY_MEMORY_BLOCK_SIZE - offset);
- dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx],
- offset, num);
- page += num;
+ if (bitmap_test_and_clear_atomic(blocks->blocks[idx], offset, 1)) {
+ if (bmap) {
+ unsigned long k = page - (ramblock->offset >> TARGET_PAGE_BITS);
+ if (!test_and_set_bit(k, bmap)) {
+ num_dirty++;
+ }
+ } else {
+ num_dirty++;
+ }
+ }
+
+ page++;
}
mr_offset = (ram_addr_t)(start_page << TARGET_PAGE_BITS) - ramblock->offset;
memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size);
}
- if (dirty) {
+ if (num_dirty) {
physical_memory_dirty_bits_cleared(start, length);
}
- return dirty;
+ return num_dirty;
}
static void physical_memory_clear_dirty_range(ram_addr_t addr, ram_addr_t length)
{
- physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION);
- physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA);
- physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE);
+ physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION, NULL);
+ physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA, NULL);
+ physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE, NULL);
}
DirtyBitmapSnapshot *physical_memory_snapshot_and_clear_dirty