From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50354) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Yi4Hd-0007Xi-7O for qemu-devel@nongnu.org; Tue, 14 Apr 2015 13:05:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Yi4Hb-00031A-3o for qemu-devel@nongnu.org; Tue, 14 Apr 2015 13:05:57 -0400 Received: from mx1.redhat.com ([209.132.183.28]:55031) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Yi4Ha-000312-OF for qemu-devel@nongnu.org; Tue, 14 Apr 2015 13:05:54 -0400 From: "Dr. David Alan Gilbert (git)" Date: Tue, 14 Apr 2015 18:04:07 +0100 Message-Id: <1429031053-4454-42-git-send-email-dgilbert@redhat.com> In-Reply-To: <1429031053-4454-1-git-send-email-dgilbert@redhat.com> References: <1429031053-4454-1-git-send-email-dgilbert@redhat.com> Subject: [Qemu-devel] [PATCH v6 41/47] Host page!=target page: Cleanup bitmaps List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: aarcange@redhat.com, yamahata@private.email.ne.jp, quintela@redhat.com, amit.shah@redhat.com, pbonzini@redhat.com, david@gibson.dropbear.id.au, yayanghy@cn.fujitsu.com From: "Dr. David Alan Gilbert" Prior to the start of postcopy, ensure that everything that will be transferred later is a whole host-page in size. This is accomplished by discarding partially transferred host pages and marking any that are partially dirty as fully dirty. Signed-off-by: Dr. David Alan Gilbert --- arch_init.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 269 insertions(+), 2 deletions(-) diff --git a/arch_init.c b/arch_init.c index dc672bf..18253af 100644 --- a/arch_init.c +++ b/arch_init.c @@ -850,7 +850,6 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage, int pages = 0; ram_addr_t dirty_ram_abs; /* Address of the start of the dirty page in ram_addr_t space */ - unsigned long hps = sysconf(_SC_PAGESIZE); if (!block) { block = QLIST_FIRST_RCU(&ram_list.blocks); @@ -867,7 +866,8 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage, * b) The last sent item was the last target-page in a host page */ if (last_was_from_queue || !last_sent_block || - ((last_offset & (hps - 1)) == (hps - TARGET_PAGE_SIZE))) { + ((last_offset & ~qemu_host_page_mask) == + (qemu_host_page_size - TARGET_PAGE_SIZE))) { tmpblock = ram_save_unqueue_page(ms, &tmpoffset, &dirty_ram_abs); } @@ -1152,6 +1152,265 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) } /* + * Helper for postcopy_chunk_hostpages where HPS/TPS >= bits-in-long + * + * !! Untested !! + */ +static int hostpage_big_chunk_helper(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, + void *opaque) +{ + MigrationState *ms = opaque; + unsigned long long_bits = sizeof(long) * 8; + unsigned int host_len = (qemu_host_page_size / TARGET_PAGE_SIZE) / + long_bits; + unsigned long first_long, last_long, cur_long, current_hp; + unsigned long first = offset >> TARGET_PAGE_BITS; + unsigned long last = (offset + (length - 1)) >> TARGET_PAGE_BITS; + + PostcopyDiscardState *pds = postcopy_discard_send_init(ms, + first, + block_name); + first_long = first / long_bits; + last_long = last / long_bits; + + /* + * I'm assuming RAMBlocks must start at the start of host pages, + * but I guess they might not use the whole of the host page + */ + + /* Work along one host page at a time */ + for (current_hp = first_long; current_hp <= last_long; + current_hp += host_len) { + bool discard = 0; + bool redirty = 0; + bool has_some_dirty = false; + bool has_some_undirty = false; + bool has_some_sent = false; + bool has_some_unsent = false; + + /* + * Check each long of mask for this hp, and see if anything + * needs updating. + */ + for (cur_long = current_hp; cur_long < (current_hp + host_len); + cur_long++) { + /* a chunk of sent pages */ + unsigned long sdata = ms->sentmap[cur_long]; + /* a chunk of dirty pages */ + unsigned long ddata = migration_bitmap[cur_long]; + + if (sdata) { + has_some_sent = true; + } + if (sdata != ~0ul) { + has_some_unsent = true; + } + if (ddata) { + has_some_dirty = true; + } + if (ddata != ~0ul) { + has_some_undirty = true; + } + + } + + if (has_some_sent && has_some_unsent) { + /* Partially sent host page */ + discard = true; + redirty = true; + } + + if (has_some_dirty && has_some_undirty) { + /* Partially dirty host page */ + redirty = true; + } + + if (!discard && !redirty) { + /* All consistent - next host page */ + continue; + } + + + /* Now walk the chunks again, sending discards etc */ + for (cur_long = current_hp; cur_long < (current_hp + host_len); + cur_long++) { + unsigned long cur_bits = cur_long * long_bits; + + /* a chunk of sent pages */ + unsigned long sdata = ms->sentmap[cur_long]; + /* a chunk of dirty pages */ + unsigned long ddata = migration_bitmap[cur_long]; + + if (discard && sdata) { + /* Tell the destination to discard these pages */ + postcopy_discard_send_range(ms, pds, cur_bits, + cur_bits + long_bits - 1); + /* And clear them in the sent data structure */ + ms->sentmap[cur_long] = 0; + } + + if (redirty) { + migration_bitmap[cur_long] = ~0ul; + /* Inc the count of dirty pages */ + migration_dirty_pages += ctpopl(~ddata); + } + } + } + + postcopy_discard_send_finish(ms, pds); + + return 0; +} + +/* + * When working on long chunks of a bitmap where the only valid section + * is between start..end (inclusive), generate a mask with only those + * valid bits set for the current long word within that bitmask. + */ +static unsigned long make_long_mask(unsigned long start, unsigned long end, + unsigned long cur_long) +{ + unsigned long long_bits = sizeof(long) * 8; + unsigned long long_bits_mask = long_bits - 1; + unsigned long first_long, last_long; + unsigned long mask = ~(unsigned long)0; + first_long = start / long_bits ; + last_long = end / long_bits; + + if ((cur_long == first_long) && (start & long_bits_mask)) { + /* e.g. (start & 31) = 3 + * 1 << . -> 2^3 + * . - 1 -> 2^3 - 1 i.e. mask 2..0 + * ~. -> mask 31..3 + */ + mask &= ~((((unsigned long)1) << (start & long_bits_mask)) - 1); + } + + if ((cur_long == last_long) && ((end & long_bits_mask) != long_bits_mask)) { + /* e.g. (end & 31) = 3 + * . +1 -> 4 + * 1 << . -> 2^4 + * . -1 -> 2^4 - 1 + * = mask set 3..0 + */ + mask &= (((unsigned long)1) << ((end & long_bits_mask) + 1)) - 1; + } + + return mask; +} + +/* + * Utility for the outgoing postcopy code. + * + * Discard any partially sent host-page size chunks, mark any partially + * dirty host-page size chunks as all dirty. + * + * Returns: 0 on success + */ +static int postcopy_chunk_hostpages(MigrationState *ms) +{ + struct RAMBlock *block; + unsigned int host_bits = qemu_host_page_size / TARGET_PAGE_SIZE; + unsigned long long_bits = sizeof(long) * 8; + unsigned long host_mask; + + assert(is_power_of_2(host_bits)); + + if (qemu_host_page_size == TARGET_PAGE_SIZE) { + /* Easy case - TPS==HPS - nothing to be done */ + return 0; + } + + /* Easiest way to make sure we don't resume in the middle of a host-page */ + last_seen_block = NULL; + last_sent_block = NULL; + + /* + * The currently worst known ratio is ARM that has 1kB target pages, and + * can have 64kB host pages, which is thus inconveniently larger than a long + * on ARM (32bits), and a long is the underlying element of the migration + * bitmaps. + */ + if (host_bits >= long_bits) { + /* Deal with the odd case separately */ + return qemu_ram_foreach_block(hostpage_big_chunk_helper, ms); + } else { + host_mask = (1ul << host_bits) - 1; + } + + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + unsigned long first_long, last_long, cur_long; + unsigned long first = block->offset >> TARGET_PAGE_BITS; + unsigned long last = (block->offset + (block->used_length - 1)) + >> TARGET_PAGE_BITS; + PostcopyDiscardState *pds = postcopy_discard_send_init(ms, + first, + block->idstr); + + first_long = first / long_bits; + last_long = last / long_bits; + for (cur_long = first_long; cur_long <= last_long; cur_long++) { + unsigned long current_hp; + /* Deal with start/end not on alignment */ + unsigned long mask = make_long_mask(first, last, cur_long); + + /* a chunk of sent pages */ + unsigned long sdata = ms->sentmap[cur_long]; + /* a chunk of dirty pages */ + unsigned long ddata = migration_bitmap[cur_long]; + unsigned long discard = 0; + unsigned long redirty = 0; + sdata &= mask; + ddata &= mask; + + for (current_hp = 0; current_hp < long_bits; + current_hp += host_bits) { + unsigned long host_sent = (sdata >> current_hp) & host_mask; + unsigned long host_dirty = (ddata >> current_hp) & host_mask; + + if (host_sent && (host_sent != host_mask)) { + /* Partially sent host page */ + redirty |= host_mask << current_hp; + discard |= host_mask << current_hp; + + /* Tell the destination to discard this page */ + postcopy_discard_send_range(ms, pds, + cur_long * long_bits + current_hp /* start */, + cur_long * long_bits + current_hp + + host_bits - 1 /* end */); + } else if (host_dirty && (host_dirty != host_mask)) { + /* Partially dirty host page */ + redirty |= host_mask << current_hp; + } + } + if (discard) { + /* clear the page in the sentmap */ + ms->sentmap[cur_long] &= ~discard; + } + if (redirty) { + /* + * Reread original dirty bits and OR in ones we clear; we + * must reread since we might be at the start or end of + * a RAMBlock that the original 'mask' discarded some + * bits from + */ + ddata = migration_bitmap[cur_long]; + migration_bitmap[cur_long] = ddata | redirty; + /* Inc the count of dirty pages */ + migration_dirty_pages += ctpopl(redirty - (ddata & redirty)); + } + } + + postcopy_discard_send_finish(ms, pds); + } + + rcu_read_unlock(); + return 0; +} + +/* * Transmit the set of pages to be discarded after precopy to the target * these are pages that have been sent previously but have been dirtied * Hopefully this is pretty sparse @@ -1161,9 +1420,17 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) int ret; rcu_read_lock(); + /* This should be our last sync, the src is now paused */ migration_bitmap_sync(); + /* Deal with TPS != HPS */ + ret = postcopy_chunk_hostpages(ms); + if (ret) { + rcu_read_unlock(); + return ret; + } + /* * Update the sentmap to be sentmap&=dirty */ -- 2.1.0