From patchwork Thu Mar 30 06:55:29 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerd Hoffmann X-Patchwork-Id: 9653081 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id DA0D2602BD for ; Thu, 30 Mar 2017 06:57:17 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CBCC42853B for ; Thu, 30 Mar 2017 06:57:17 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C0A8F28541; Thu, 30 Mar 2017 06:57:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3A7192853B for ; Thu, 30 Mar 2017 06:57:17 +0000 (UTC) Received: from localhost ([::1]:34003 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctU1E-0005sW-Eu for patchwork-qemu-devel@patchwork.kernel.org; Thu, 30 Mar 2017 02:57:16 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52659) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctU02-0005nl-My for qemu-devel@nongnu.org; Thu, 30 Mar 2017 02:56:04 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctTzy-0005d1-Rq for qemu-devel@nongnu.org; Thu, 30 Mar 2017 02:56:02 -0400 Received: from mx1.redhat.com ([209.132.183.28]:58872) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctTzy-0005b0-Hg for qemu-devel@nongnu.org; Thu, 30 Mar 2017 02:55:58 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 997D0C056791; Thu, 30 Mar 2017 06:55:56 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 997D0C056791 Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=kraxel@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 997D0C056791 Received: from nilsson.home.kraxel.org (ovpn-116-80.ams2.redhat.com [10.36.116.80]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3223418136; Thu, 30 Mar 2017 06:55:54 +0000 (UTC) Received: by nilsson.home.kraxel.org (Postfix, from userid 500) id EAFFE80FD6; Thu, 30 Mar 2017 08:55:52 +0200 (CEST) From: Gerd Hoffmann To: qemu-devel@nongnu.org Date: Thu, 30 Mar 2017 08:55:29 +0200 Message-Id: <1490856931-21732-3-git-send-email-kraxel@redhat.com> In-Reply-To: <1490856931-21732-1-git-send-email-kraxel@redhat.com> References: <1490856931-21732-1-git-send-email-kraxel@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Thu, 30 Mar 2017 06:55:56 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RfC PATCH 2/4] memory: add support getting and using a dirty bitmap copy. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , Richard Henderson , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Gerd Hoffmann , Peter Crosthwaite Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP This patch adds support for getting and using a local copy of the dirty bitmap. memory_region_copy_and_clear_dirty() will create a copy of the dirty bitmap for the specified range, clear the dirty bitmap and return the copy. The returned bitmap can be a bit larger than requested, the range is expanded so the code can copy unsigned longs from the bitmap and avoid atomic bit update operations. memory_region_copy_get_dirty() will return the dirty status of pages, pretty much like memory_region_get_dirty(), but using the copy returned by memory_region_copy_and_clear_dirty(). Signed-off-by: Gerd Hoffmann --- exec.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ include/exec/memory.h | 13 ++++++++ include/exec/ram_addr.h | 8 +++++ include/qemu/typedefs.h | 1 + memory.c | 15 ++++++++++ 5 files changed, 116 insertions(+) diff --git a/exec.c b/exec.c index e57a8a2..57c5bc9 100644 --- a/exec.c +++ b/exec.c @@ -223,6 +223,12 @@ struct CPUAddressSpace { MemoryListener tcg_as_listener; }; +struct DirtyCopy { + ram_addr_t start; + ram_addr_t end; + unsigned long dirty[]; +}; + #endif #if !defined(CONFIG_USER_ONLY) @@ -1061,6 +1067,79 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, return dirty; } +DirtyCopy *cpu_physical_memory_copy_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client) +{ + DirtyMemoryBlocks *blocks; + unsigned long align = 1 << (TARGET_PAGE_BITS + BITS_PER_LEVEL); + ram_addr_t first = QEMU_ALIGN_DOWN(start, align); + ram_addr_t last = QEMU_ALIGN_UP(start + length, align); + DirtyCopy *copy; + unsigned long page, end, dest; + + copy = g_malloc0(sizeof(*copy) + + ((last - first) >> (TARGET_PAGE_BITS + 3))); + copy->start = first; + copy->end = last; + + page = first >> TARGET_PAGE_BITS; + end = last >> TARGET_PAGE_BITS; + dest = 0; + + rcu_read_lock(); + + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + 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); + + assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); + assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); + offset >>= BITS_PER_LEVEL; + + bitmap_copy(copy->dirty + dest, + blocks->blocks[idx] + (offset >> BITS_PER_LEVEL), + num); + bitmap_zero(blocks->blocks[idx] + (offset >> BITS_PER_LEVEL), + num); + + page += num; + dest += num >> BITS_PER_LEVEL; + } + + rcu_read_unlock(); + + if (tcg_enabled()) { + tlb_reset_dirty_range_all(start, length); + } + + return copy; +} + +bool cpu_physical_memory_copy_get_dirty(DirtyCopy *copy, + ram_addr_t start, + ram_addr_t length) +{ + unsigned long page, end; + + assert(start > copy->start); + assert(start + length < copy->end); + + end = TARGET_PAGE_ALIGN(start + length - copy->start) >> TARGET_PAGE_BITS; + page = (start - copy->start) >> TARGET_PAGE_BITS; + + while (page < end) { + if (test_bit(page, copy->dirty)) { + return true; + } + page++; + } + return false; +} + /* Called from RCU critical section */ hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section, diff --git a/include/exec/memory.h b/include/exec/memory.h index e39256a..f89cfa7 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -871,6 +871,19 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, */ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client); + +/** + * memory_region_copy_and_clear_dirty: TODO + */ +DirtyCopy *memory_region_copy_and_clear_dirty(MemoryRegion *mr, hwaddr addr, + hwaddr size, unsigned client); + +/** + * memory_region_copy_get_dirty: TODO + */ +bool memory_region_copy_get_dirty(MemoryRegion *mr, DirtyCopy *copy, + hwaddr addr, hwaddr size); + /** * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with * any external TLBs (e.g. kvm) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index b05dc84..3886f0a 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -343,6 +343,14 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client); +DirtyCopy *cpu_physical_memory_copy_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client); + +bool cpu_physical_memory_copy_get_dirty(DirtyCopy *copy, + ram_addr_t start, + ram_addr_t length); + static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, ram_addr_t length) { diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index e95f28c..5344aee 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -23,6 +23,7 @@ typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUState CPUState; typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; +typedef struct DirtyCopy DirtyCopy; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DisplayState DisplayState; typedef struct DisplaySurface DisplaySurface; diff --git a/memory.c b/memory.c index 4c95aaf..7d75ca6 100644 --- a/memory.c +++ b/memory.c @@ -1716,6 +1716,21 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, memory_region_get_ram_addr(mr) + addr, size, client); } +DirtyCopy *memory_region_copy_and_clear_dirty(MemoryRegion *mr, hwaddr addr, + hwaddr size, unsigned client) +{ + assert(mr->ram_block); + return cpu_physical_memory_copy_and_clear_dirty( + memory_region_get_ram_addr(mr) + addr, size, client); +} + +bool memory_region_copy_get_dirty(MemoryRegion *mr, DirtyCopy *copy, + hwaddr addr, hwaddr size) +{ + assert(mr->ram_block); + return cpu_physical_memory_copy_get_dirty(copy, + memory_region_get_ram_addr(mr) + addr, size); +} void memory_region_sync_dirty_bitmap(MemoryRegion *mr) {