diff options
-rw-r--r-- | tools/libxc/xenctrl.h | 20 | ||||
-rw-r--r-- | xen/arch/x86/hvm/hvm.c | 8 | ||||
-rw-r--r-- | xen/arch/x86/mm/Makefile | 1 | ||||
-rw-r--r-- | xen/arch/x86/mm/dirty_vram.c | 864 | ||||
-rw-r--r-- | xen/arch/x86/mm/hap/hap.c | 111 | ||||
-rw-r--r-- | xen/arch/x86/mm/p2m.c | 11 | ||||
-rw-r--r-- | xen/arch/x86/mm/paging.c | 57 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/common.c | 335 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/multi.c | 174 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/multi.h | 7 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/types.h | 1 | ||||
-rw-r--r-- | xen/include/asm-x86/dirty_vram.h | 202 | ||||
-rw-r--r-- | xen/include/asm-x86/hap.h | 4 | ||||
-rw-r--r-- | xen/include/asm-x86/hvm/domain.h | 2 | ||||
-rw-r--r-- | xen/include/asm-x86/paging.h | 15 | ||||
-rw-r--r-- | xen/include/asm-x86/shadow.h | 6 |
16 files changed, 1421 insertions, 397 deletions
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index 32122fd303..cd4e1ef0d8 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -1563,15 +1563,23 @@ int xc_hvm_inject_msi( xc_interface *xch, domid_t dom, uint64_t addr, uint32_t data); /* - * Track dirty bit changes in the VRAM area + * Track dirty bit changes in a VRAM region defined by + * [ first_pfn : first_pfn + nr - 1 ] * * All of this is done atomically: - * - get the dirty bitmap since the last call - * - set up dirty tracking area for period up to the next call - * - clear the dirty tracking area. + * - gets the dirty bitmap since the last call, all zeroes for + * the first call with some new region + * - sets up a dirty tracking region for period up to the next call + * - clears the specified dirty tracking region. * - * Returns -ENODATA and does not fill bitmap if the area has changed since the - * last call. + * Creating a new region causes any existing regions that it overlaps + * to be discarded. + * + * Specifying nr == 0 causes all regions to be discarded and + * disables dirty bit tracking. + * + * If nr is not a multiple of 64, only the first nr bits of bitmap + * are well defined. */ int xc_hvm_track_dirty_vram( xc_interface *xch, domid_t dom, diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 374a74046f..e8e27926cc 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -57,6 +57,7 @@ #include <asm/hvm/cacheattr.h> #include <asm/hvm/trace.h> #include <asm/hvm/nestedhvm.h> +#include <asm/dirty_vram.h> #include <asm/mtrr.h> #include <asm/apic.h> #include <public/sched.h> @@ -1436,8 +1437,11 @@ int hvm_hap_nested_page_fault(paddr_t gpa, */ if ( access_w ) { - paging_mark_dirty(v->domain, mfn_x(mfn)); - p2m_change_type(v->domain, gfn, p2m_ram_logdirty, p2m_ram_rw); + if ( p2m_change_type(v->domain, gfn, p2m_ram_logdirty, + p2m_ram_rw) == p2m_ram_logdirty ) + { + paging_mark_dirty_gpfn(v->domain, gfn); + } } rc = 1; goto out_put_gfn; diff --git a/xen/arch/x86/mm/Makefile b/xen/arch/x86/mm/Makefile index 73dcdf4cb1..becd0c9bea 100644 --- a/xen/arch/x86/mm/Makefile +++ b/xen/arch/x86/mm/Makefile @@ -5,6 +5,7 @@ obj-y += paging.o obj-y += p2m.o p2m-pt.o p2m-ept.o p2m-pod.o obj-y += guest_walk_2.o obj-y += guest_walk_3.o +obj-y += dirty_vram.o obj-$(x86_64) += guest_walk_4.o obj-$(x86_64) += mem_event.o obj-$(x86_64) += mem_paging.o diff --git a/xen/arch/x86/mm/dirty_vram.c b/xen/arch/x86/mm/dirty_vram.c new file mode 100644 index 0000000000..68e39731ba --- /dev/null +++ b/xen/arch/x86/mm/dirty_vram.c @@ -0,0 +1,864 @@ +/* + * arch/x86/mm/dirty_vram.c: Bookkeep/query dirty VRAM pages + * with support for multiple frame buffers. + * + * Copyright (c) 2012, Citrix Systems, Inc. (Robert Phillips) + * Parts of this code are Copyright (c) 2007 Advanced Micro Devices (Wei Huang) + * Parts of this code are Copyright (c) 2007 XenSource Inc. + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + + +#include <xen/types.h> +#include <xen/sched.h> +#include <xen/guest_access.h> +#include <asm/shadow.h> +#include <asm/dirty_vram.h> +#include <asm/hap.h> +#include <asm/config.h> +#include "mm-locks.h" + +#define DEBUG_stop_tracking_all_vram 0 +#define DEBUG_allocating_dirty_vram_range 0 +#define DEBUG_high_water_mark_for_vram_ranges 0 +#define DEBUG_freeing_dirty_vram_range 0 +#define DEBUG_allocate_paddr_links_page 0 +#define DEBUG_update_vram_mapping 0 +#define DEBUG_alloc_paddr_inject_fault 0 +#define DEBUG_link_limit_exceeded 0 + +/* Allocates domain's dirty_vram structure */ +dv_dirty_vram_t * +dirty_vram_alloc(struct domain *d) +{ + dv_dirty_vram_t *dirty_vram; + ASSERT( paging_locked_by_me(d) ); + dirty_vram = d->arch.hvm_domain.dirty_vram = xzalloc(dv_dirty_vram_t); + if ( dirty_vram ) + { + INIT_LIST_HEAD(&dirty_vram->range_head); + INIT_LIST_HEAD(&dirty_vram->ext_head); + } + return dirty_vram; +} + +/* + * Returns domain's dirty_vram structure, + * allocating it if necessary + */ +dv_dirty_vram_t * +dirty_vram_find_or_alloc(struct domain *d) +{ + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + ASSERT( paging_locked_by_me(d) ); + if ( !dirty_vram ) + dirty_vram = dirty_vram_alloc(d); + return dirty_vram; +} + + +/* Free domain's dirty_vram structure */ +void dirty_vram_free(struct domain *d) +{ + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + ASSERT( paging_locked_by_me(d) ); + if ( dirty_vram ) + { + struct list_head *curr, *next; + /* Free all the ranges */ + list_for_each_safe(curr, next, &dirty_vram->range_head) + { + dv_range_t *range = list_entry(curr, dv_range_t, range_link); +#if DEBUG_stop_tracking_all_vram + gdprintk(XENLOG_DEBUG, "[%05lx:%05lx] stop tracking all vram\n", + range->begin_pfn, range->end_pfn); +#endif + xfree(range->pl_tab); + xfree(range); + } + /* Free all the extension pages */ + list_for_each_safe(curr, next, &dirty_vram->ext_head) + { + struct dv_paddr_link_ext *ext = + container_of( + curr, struct dv_paddr_link_ext, ext_link); + struct page_info *pg = __virt_to_page(ext); + d->arch.paging.free_page(d, pg); + } + + xfree(dirty_vram); + d->arch.hvm_domain.dirty_vram = NULL; + } +} + +/* Returns dirty vram range containing gfn, NULL if none */ +struct dv_range * +dirty_vram_range_find_gfn(struct domain *d, + unsigned long gfn) +{ + struct dv_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; + ASSERT( paging_locked_by_me(d) ); + if ( dirty_vram ) + { + struct list_head *curr; + list_for_each(curr, &dirty_vram->range_head) + { + dv_range_t *range = list_entry(curr, dv_range_t, range_link); + if ( gfn >= range->begin_pfn && + gfn < range->end_pfn ) + return range; + } + } + return NULL; +} + +/* + * Returns pointer to dirty vram range matching [begin_pfn .. end_pfn ), + * NULL if none. + */ +dv_range_t * +dirty_vram_range_find(struct domain *d, + unsigned long begin_pfn, + unsigned long nr) +{ + unsigned long end_pfn = begin_pfn + nr; + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + ASSERT( paging_locked_by_me(d) ); + if ( dirty_vram ) + { + struct list_head *curr; + list_for_each(curr, &dirty_vram->range_head) + { + dv_range_t *range = list_entry(curr, dv_range_t, range_link); + if ( begin_pfn == range->begin_pfn && + end_pfn == range->end_pfn ) + return range; + } + } + return NULL; +} + +/* Allocate specified dirty_vram range */ +static dv_range_t * +_dirty_vram_range_alloc(struct domain *d, + unsigned long begin_pfn, + unsigned long nr) +{ + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + dv_range_t *range = NULL; + unsigned long end_pfn = begin_pfn + nr; + dv_pl_entry_t *pl_tab = NULL; + int i; + + ASSERT( paging_locked_by_me(d) ); + ASSERT( dirty_vram != NULL ); + +#if DEBUG_allocating_dirty_vram_range + gdprintk(XENLOG_DEBUG, + "[%05lx:%05lx] Allocating dirty vram range hap:%d\n", + begin_pfn, end_pfn, + d->arch.hvm_domain.hap_enabled); +#endif + + range = xzalloc(dv_range_t); + if ( range == NULL ) + goto err_out; + + INIT_LIST_HEAD(&range->range_link); + + range->begin_pfn = begin_pfn; + range->end_pfn = end_pfn; + + if ( !hap_enabled(d) ) + { + if ( (pl_tab = xzalloc_array(dv_pl_entry_t, nr)) == NULL ) + goto err_out; + + for ( i = 0; i != nr; i++ ) + { + pl_tab[i].mapping.sl1ma = INVALID_PADDR; + } + } + + range->pl_tab = pl_tab; + range->mappings_hwm = 1; + + list_add(&range->range_link, &dirty_vram->range_head); + if ( ++dirty_vram->nr_ranges > dirty_vram->ranges_hwm ) + { + dirty_vram->ranges_hwm = dirty_vram->nr_ranges; +#if DEBUG_high_water_mark_for_vram_ranges + gdprintk(XENLOG_DEBUG, + "High water mark for number of vram ranges is now:%d\n", + dirty_vram->ranges_hwm); +#endif + } + return range; + + err_out: + xfree(pl_tab); + xfree(range); + return NULL; +} + + +/* Frees specified dirty_vram range */ +void dirty_vram_range_free(struct domain *d, + dv_range_t *range) +{ + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + ASSERT( paging_locked_by_me(d) ); + if ( dirty_vram ) + { + int i, nr = range->end_pfn - range->begin_pfn; + +#if DEBUG_freeing_dirty_vram_range + gdprintk(XENLOG_DEBUG, + "[%05lx:%05lx] Freeing dirty vram range\n", + range->begin_pfn, range->end_pfn); +#endif + + if ( range->pl_tab ) + { + for ( i = 0; i != nr; i++ ) + { + dv_paddr_link_t *plx; + plx = range->pl_tab[i].mapping.pl_next; + /* Does current FB page have multiple mappings? */ + if ( plx ) /* yes */ + { + /* Find the last element in singly-linked list */ + while ( plx->pl_next != NULL ) + plx = plx->pl_next; + + /* Prepend whole list to the free list */ + plx->pl_next = dirty_vram->pl_free; + dirty_vram->pl_free = range->pl_tab[i].mapping.pl_next; + } + } + xfree(range->pl_tab); + range->pl_tab = NULL; + } + + /* Remove range from the linked list, free it, and adjust count*/ + list_del(&range->range_link); + xfree(range); + dirty_vram->nr_ranges--; + } +} + +/* + * dirty_vram_range_alloc() + * This function ensures that the new range does not overlap any existing + * ranges -- deleting them if necessary -- and then calls + * _dirty_vram_range_alloc to actually allocate the new range. + */ +dv_range_t * +dirty_vram_range_alloc(struct domain *d, + unsigned long begin_pfn, + unsigned long nr) +{ + unsigned long end_pfn = begin_pfn + nr; + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + dv_range_t *range; + struct list_head *curr, *next; + + ASSERT( paging_locked_by_me(d) ); + ASSERT( dirty_vram != NULL ); + + /* + * Ranges cannot overlap so + * free any range that overlaps [ begin_pfn .. end_pfn ) + */ + list_for_each_safe(curr, next, &dirty_vram->range_head) + { + dv_range_t *rng = list_entry(curr, dv_range_t, range_link); + if ( ( ( rng->begin_pfn <= begin_pfn ) && + ( begin_pfn < rng->end_pfn ) + ) || + ( ( begin_pfn <= rng->begin_pfn ) && + ( rng->begin_pfn < end_pfn ) + ) ) + { + /* Different tracking, tear the previous down. */ + dirty_vram_range_free(d, rng); + } + } + + range = _dirty_vram_range_alloc(d, begin_pfn, nr); + if ( !range ) + goto out; + + out: + return range; +} + +/* + * dirty_vram_range_find_or_alloc() + * Find the range for [begin_pfn:begin_pfn+nr). + * If it doesn't exists, create it. + */ +dv_range_t * +dirty_vram_range_find_or_alloc(struct domain *d, + unsigned long begin_pfn, + unsigned long nr) +{ + dv_range_t *range; + ASSERT( paging_locked_by_me(d) ); + range = dirty_vram_range_find(d, begin_pfn, nr); + if ( !range ) + range = dirty_vram_range_alloc(d, begin_pfn, nr); + + return range; +} + + + +/* Allocate a dv_paddr_link struct */ +static dv_paddr_link_t * +alloc_paddr_link(struct domain *d) +{ + dv_paddr_link_t * pl = NULL; + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + dv_paddr_link_ext_t *ext = NULL; + + + ASSERT( paging_locked_by_me(d) ); + BUILD_BUG_ON(sizeof(dv_paddr_link_ext_t) > PAGE_SIZE); + /* Is the list of free pl's empty? */ + if ( dirty_vram->pl_free == NULL ) /* yes */ + { + /* + * Allocate another page of pl's. + * Link them all together and point the free list head at them + */ + int i; + struct page_info *pg = d->arch.paging.alloc_page(d); + + ext = map_domain_page(pg); + if ( ext == NULL ) + goto out; + +#if DEBUG_allocate_paddr_links_page + gdprintk(XENLOG_DEBUG, "Allocated another page of paddr_links\n"); +#endif + list_add(&ext->ext_link, &dirty_vram->ext_head); + + /* initialize and link together the new pl entries */ + for ( i = 0; i != ARRAY_SIZE(ext->entries); i++ ) + { + ext->entries[i].sl1ma = INVALID_PADDR; + ext->entries[i].pl_next = &ext->entries[i+1]; + } + ext->entries[ARRAY_SIZE(ext->entries) - 1].pl_next = NULL; + dirty_vram->pl_free = &ext->entries[0]; + } + pl = dirty_vram->pl_free; + dirty_vram->pl_free = pl->pl_next; + + pl->sl1ma = INVALID_PADDR; + pl->pl_next = NULL; + out: + if ( ext ) + unmap_domain_page(ext); + + return pl; +} + + +/* + * Free a paddr_link struct. + * + * The caller has walked the singly-linked list of elements + * that have, as their head, an element in a pl_tab cell. + * The list walks has reached the element to be freed. + * (Each element is a dv_paddr_link_t struct.) + * + * @pl points to the element to be freed. + * @ppl points to its predecessor element's next member. + * + * After linking the precessor to the element's successor, + * we can free @pl by prepending it to the list of free + * elements. + * + * As a boundary case (which happens to be the common case), + * @pl points to a cell in the pl_tab rather than to some + * extension element danging from that cell. + * We recognize this case because @ppl is NULL. + * In that case we promote the first extension element by + * copying it into the pl_tab cell and free it. + */ + +dv_paddr_link_t * +free_paddr_link(struct domain *d, + dv_paddr_link_t **ppl, + dv_paddr_link_t *pl) +{ + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; + dv_paddr_link_t *npl; /* next pl */ + + ASSERT( paging_locked_by_me(d) ); + /* extension mapping? */ + if ( ppl ) /* yes. free it */ + { + ASSERT(pl == (*ppl)); + (*ppl) = npl = pl->pl_next; + } + else /* main table */ + { + /* + * move 2nd mapping to main table. + * and free 2nd mapping + */ + dv_paddr_link_t * spl; + spl = pl->pl_next; + if ( spl == NULL ) + { + pl->sl1ma = INVALID_PADDR; + return pl; + } + pl->sl1ma = spl->sl1ma; + pl->pl_next = spl->pl_next; + npl = pl; /* reprocess main table entry again */ + pl = spl; + } + pl->sl1ma = INVALID_PADDR; + pl->pl_next = dirty_vram->pl_free; + dirty_vram->pl_free = pl; + return npl; +} + + +/* + * dirty_vram_range_update() + * + * This is called whenever a level 1 page table entry is modified. + * If the L1PTE is being cleared, the function removes any paddr_links + * that refer to it. + * If the L1PTE is being set to a frame buffer page, a paddr_link is + * created for that page's entry in pl_tab. + * Returns 1 iff entry found and set or cleared. + */ +int dirty_vram_range_update(struct domain *d, + unsigned long gfn, + paddr_t sl1ma, + int set) +{ + int effective = 0; + dv_range_t *range; + unsigned long i; + dv_paddr_link_t *pl; + dv_paddr_link_t **ppl; + int len = 0; + + ASSERT(paging_locked_by_me(d)); + range = dirty_vram_range_find_gfn(d, gfn); + if ( !range ) + return effective; + + + i = gfn - range->begin_pfn; + pl = &range->pl_tab[ i ].mapping; + ppl = NULL; + + /* + * find matching entry (pl), if any, and its predecessor + * in linked list (ppl) + */ + while ( pl != NULL ) + { + if ( pl->sl1ma == sl1ma || pl->sl1ma == INVALID_PADDR ) + break; + + ppl = &pl->pl_next; + pl = *ppl; + len++; + } + + if ( set ) + { + /* Did we find sl1ma in either the main table or the linked list? */ + if ( pl == NULL ) /* no, so we'll need to alloc a link */ + { + ASSERT(ppl != NULL); + +#if DEBUG_alloc_paddr_inject_fault + { + static int counter; + + /* Test stuck_dirty logic for some cases */ + if ( (++counter) % 4 == 0 ) + { + /* Simply mark the frame buffer page as always dirty */ + range->pl_tab[ i ].stuck_dirty = 1; + gdprintk(XENLOG_DEBUG, + "[%lx] inject stuck dirty fault\n", + gfn ); + goto out; + } + } +#endif + /* + * Have we reached the limit of mappings we're willing + * to bookkeep? + */ + if ( len > DV_ADDR_LINK_LIST_LIMIT ) /* yes */ + { +#if DEBUG_link_limit_exceeded + if ( !range->pl_tab[ i ].stuck_dirty ) + gdprintk(XENLOG_DEBUG, + "[%lx] link limit exceeded\n", + gfn ); +#endif + /* Simply mark the frame buffer page as always dirty */ + range->pl_tab[ i ].stuck_dirty = 1; + goto out; + } + + /* alloc link and append it to list */ + (*ppl) = pl = alloc_paddr_link(d); + /* Were we able to allocate a link? */ + if ( pl == NULL ) /* no */ + { + /* Simply mark the frame buffer page as always dirty */ + range->pl_tab[ i ].stuck_dirty = 1; + + gdprintk(XENLOG_DEBUG, + "[%lx] alloc failure\n", + gfn ); + + goto out; + } + } + if ( pl->sl1ma != sl1ma ) + { + ASSERT(pl->sl1ma == INVALID_PADDR); + pl->sl1ma = sl1ma; + range->nr_mappings++; + } + effective = 1; + if ( len > range->mappings_hwm ) + { + range->mappings_hwm = len; +#if DEBUG_update_vram_mapping + gdprintk(XENLOG_DEBUG, + "[%lx] set sl1ma:%lx hwm:%d mappings:%d " + "freepages:%d\n", + gfn, sl1ma, + range->mappings_hwm, + range->nr_mappings, + d->arch.paging.shadow.free_pages); +#endif + } + } + else /* clear */ + { + if ( pl && pl->sl1ma == sl1ma ) + { +#if DEBUG_update_vram_mapping + gdprintk(XENLOG_DEBUG, + "[%lx] clear sl1ma:%lx mappings:%d\n", + gfn, sl1ma, + range->nr_mappings - 1); +#endif + free_paddr_link(d, ppl, pl); + --range->nr_mappings; + effective = 1; + } + } + out: + return effective; +} + + +/* + * shadow_scan_dirty_flags() + * This produces a dirty bitmap for the range by examining every + * L1PTE referenced by some dv_paddr_link in the range's pl_tab table. + * It tests and clears each such L1PTE's dirty flag. + */ +static int shadow_scan_dirty_flags(struct domain *d, + dv_range_t *range, + uint8_t *dirty_bitmap) +{ + int flush_tlb = 0; + unsigned long i; + unsigned long nr = range->end_pfn - range->begin_pfn; + l1_pgentry_t *sl1e = NULL; + + ASSERT( paging_locked_by_me(d) ); + /* Iterate over VRAM to track dirty bits. */ + for ( i = 0; i < nr; i++ ) + { + int dirty = 0, len = 1; + dv_paddr_link_t *pl; + /* Does the frame buffer have an incomplete set of mappings? */ + if ( unlikely(range->pl_tab[i].stuck_dirty) ) /* yes */ + dirty = 1; + else /* The frame buffer's set of mappings is complete. Scan it. */ + for ( pl = &range->pl_tab[i].mapping; + pl; + pl = pl->pl_next, len++ ) + { + paddr_t sl1ma = pl->sl1ma; + if ( sl1ma == INVALID_PADDR ) /* FB page is unmapped */ + continue; + + if ( sl1e ) /* cleanup from previous iteration */ + unmap_domain_page(sl1e); + + sl1e = map_domain_page(sl1ma >> PAGE_SHIFT); + if ( l1e_get_flags(*sl1e) & _PAGE_DIRTY ) + { + dirty = 1; + /* Clear dirty so we can detect if page gets re-dirtied. + * Note: this is atomic, so we may clear a + * _PAGE_ACCESSED set by another processor. + */ + l1e_remove_flags(*sl1e, _PAGE_DIRTY); + flush_tlb = 1; + } + } /* for */ + + if ( dirty ) + dirty_bitmap[i >> 3] |= (1 << (i & 7)); + + } + + if ( sl1e ) + unmap_domain_page(sl1e); + + return flush_tlb; +} + + +/* + * shadow_track_dirty_vram() + * This is the API called by the guest to determine which pages in the range + * from [begin_pfn:begin_pfn+nr) have been dirtied since the last call. + * It creates the domain's dv_dirty_vram on demand. + * It creates ranges on demand when some [begin_pfn:nr) is first encountered. + * To collect the dirty bitmask it calls shadow_scan_dirty_flags(). + * It copies the dirty bitmask into guest storage. + */ +int shadow_track_dirty_vram(struct domain *d, + unsigned long begin_pfn, + unsigned long nr, + XEN_GUEST_HANDLE_64(uint8) guest_dirty_bitmap) +{ + int rc = 0; + unsigned long end_pfn = begin_pfn + nr; + int flush_tlb = 0; + dv_range_t *range; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + uint8_t *dirty_bitmap = NULL; + + /* + * This range test is tricky. + * + * The range [begin_pfn..end_pfn) is an open interval, so end_pfn + * is a pfn beyond the end of the range. + * + * p2m->max_mapped_pfn is a valid PFN so p2m->max_mapped_pfn + 1 is an + * invalid PFN. + * + * If end_pfn is beyond *that* then the range is invalid. + */ + if ( end_pfn < begin_pfn + || begin_pfn > p2m->max_mapped_pfn + || end_pfn > p2m->max_mapped_pfn + 1 ) + return -EINVAL; + + paging_lock(d); + + if ( !nr ) + { + dirty_vram_free(d); + goto out; + } + + if ( guest_handle_is_null(guest_dirty_bitmap) ) + goto out; + + if ( !dirty_vram_find_or_alloc(d) ) + { + rc = -ENOMEM; + goto out; + } + + range = dirty_vram_range_find(d, begin_pfn, nr); + if ( !range ) + { + range = dirty_vram_range_alloc(d, begin_pfn, nr); + if ( range ) + sh_find_all_vram_mappings(d->vcpu[0], range); + } + if ( range ) + { + int size = ( nr + BITS_PER_BYTE - 1 ) / BITS_PER_BYTE; + + rc = -ENOMEM; + dirty_bitmap = xzalloc_bytes( size ); + if ( !dirty_bitmap ) + goto out; + + flush_tlb |= shadow_scan_dirty_flags(d, range, dirty_bitmap); + + rc = -EFAULT; + if ( copy_to_guest(guest_dirty_bitmap, + dirty_bitmap, + size) == 0 ) + rc = 0; + } + + if ( flush_tlb ) + flush_tlb_mask(d->domain_dirty_cpumask); + +out: + paging_unlock(d); + + if ( dirty_bitmap ) + xfree(dirty_bitmap); + return rc; +} + + +/************************************************/ +/* HAP VRAM TRACKING SUPPORT */ +/************************************************/ + +/* + * hap_track_dirty_vram() + * Create the domain's dv_dirty_vram struct on demand. + * Create a dirty vram range on demand when some [begin_pfn:begin_pfn+nr] is + * first encountered. + * Collect the guest_dirty bitmask, a bit mask of the dirty vram pages, by + * calling paging_log_dirty_range(), which interrogates each vram + * page's p2m type looking for pages that have been made writable. + */ +int hap_track_dirty_vram(struct domain *d, + unsigned long begin_pfn, + unsigned long nr, + XEN_GUEST_HANDLE_64(uint8) guest_dirty_bitmap) +{ + long rc = 0; + dv_dirty_vram_t *dirty_vram; + uint8_t *dirty_bitmap = NULL; + + if ( nr ) + { + dv_range_t *range = NULL; + int size = ( nr + BITS_PER_BYTE - 1 ) / BITS_PER_BYTE; + + if ( !paging_mode_log_dirty(d) ) + { + hap_logdirty_init(d); + rc = paging_log_dirty_enable(d); + if ( rc ) + goto out; + } + + rc = -ENOMEM; + dirty_bitmap = xzalloc_bytes( size ); + if ( !dirty_bitmap ) + goto out; + + paging_lock(d); + + dirty_vram = d->arch.hvm_domain.dirty_vram; + if ( !dirty_vram ) + { + rc = -ENOMEM; + if ( !(dirty_vram = dirty_vram_alloc(d)) ) + { + paging_unlock(d); + goto out; + } + } + + range = dirty_vram_range_find(d, begin_pfn, nr); + if ( !range ) + { + rc = -ENOMEM; + if ( !(range = dirty_vram_range_alloc(d, begin_pfn, nr)) ) + { + paging_unlock(d); + goto out; + } + + paging_unlock(d); + + /* set l1e entries of range within P2M table to be read-only. */ + p2m_change_type_range(d, begin_pfn, begin_pfn + nr, + p2m_ram_rw, p2m_ram_logdirty); + + flush_tlb_mask(d->domain_dirty_cpumask); + + memset(dirty_bitmap, 0xff, size); /* consider all pages dirty */ + } + else + { + paging_unlock(d); + + domain_pause(d); + + /* get the bitmap */ + paging_log_dirty_range(d, begin_pfn, nr, dirty_bitmap); + + domain_unpause(d); + } + + + rc = -EFAULT; + if ( copy_to_guest(guest_dirty_bitmap, + dirty_bitmap, + size) == 0 ) + { + rc = 0; + } + } + else { + paging_lock(d); + + dirty_vram = d->arch.hvm_domain.dirty_vram; + if ( dirty_vram ) + { + /* + * If zero pages specified while tracking dirty vram + * then stop tracking + */ + dirty_vram_free(d); + + } + + paging_unlock(d); + } +out: + if ( dirty_bitmap ) + xfree(dirty_bitmap); + + return rc; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c index a95ccbf62c..f7d979bcdc 100644 --- a/xen/arch/x86/mm/hap/hap.c +++ b/xen/arch/x86/mm/hap/hap.c @@ -53,117 +53,6 @@ #define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg)) /************************************************/ -/* HAP VRAM TRACKING SUPPORT */ -/************************************************/ - -/* - * hap_track_dirty_vram() - * Create the domain's dv_dirty_vram struct on demand. - * Create a dirty vram range on demand when some [begin_pfn:begin_pfn+nr] is - * first encountered. - * Collect the guest_dirty bitmask, a bit mask of the dirty vram pages, by - * calling paging_log_dirty_range(), which interrogates each vram - * page's p2m type looking for pages that have been made writable. - */ - -int hap_track_dirty_vram(struct domain *d, - unsigned long begin_pfn, - unsigned long nr, - XEN_GUEST_HANDLE_64(uint8) guest_dirty_bitmap) -{ - long rc = 0; - struct sh_dirty_vram *dirty_vram; - uint8_t *dirty_bitmap = NULL; - - if ( nr ) - { - int size = (nr + BITS_PER_BYTE - 1) / BITS_PER_BYTE; - - if ( !paging_mode_log_dirty(d) ) - { - hap_logdirty_init(d); - rc = paging_log_dirty_enable(d); - if ( rc ) - goto out; - } - - rc = -ENOMEM; - dirty_bitmap = xzalloc_bytes(size); - if ( !dirty_bitmap ) - goto out; - - paging_lock(d); - - dirty_vram = d->arch.hvm_domain.dirty_vram; - if ( !dirty_vram ) - { - rc = -ENOMEM; - if ( (dirty_vram = xzalloc(struct sh_dirty_vram)) == NULL ) - { - paging_unlock(d); - goto out; - } - - d->arch.hvm_domain.dirty_vram = dirty_vram; - } - - if ( begin_pfn != dirty_vram->begin_pfn || - begin_pfn + nr != dirty_vram->end_pfn ) - { - dirty_vram->begin_pfn = begin_pfn; - dirty_vram->end_pfn = begin_pfn + nr; - - paging_unlock(d); - - /* set l1e entries of range within P2M table to be read-only. */ - p2m_change_type_range(d, begin_pfn, begin_pfn + nr, - p2m_ram_rw, p2m_ram_logdirty); - - flush_tlb_mask(d->domain_dirty_cpumask); - - memset(dirty_bitmap, 0xff, size); /* consider all pages dirty */ - } - else - { - paging_unlock(d); - - domain_pause(d); - - /* get the bitmap */ - paging_log_dirty_range(d, begin_pfn, nr, dirty_bitmap); - - domain_unpause(d); - } - - rc = -EFAULT; - if ( copy_to_guest(guest_dirty_bitmap, dirty_bitmap, size) == 0 ) - rc = 0; - } - else - { - paging_lock(d); - - dirty_vram = d->arch.hvm_domain.dirty_vram; - if ( dirty_vram ) - { - /* - * If zero pages specified while tracking dirty vram - * then stop tracking - */ - xfree(dirty_vram); - d->arch.hvm_domain.dirty_vram = NULL; - } - - paging_unlock(d); - } -out: - if ( dirty_bitmap ) - xfree(dirty_bitmap); - - return rc; -} - -/************************************************/ /* HAP LOG DIRTY SUPPORT */ /************************************************/ diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index de1dd822ea..6f638a27d1 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -741,20 +741,23 @@ void p2m_change_type_range(struct domain *d, struct p2m_domain *p2m = p2m_get_hostp2m(d); BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); - p2m_lock(p2m); - p2m->defer_nested_flush = 1; + p2m->defer_nested_flush = 1; + for ( gfn = start; gfn < end; gfn++ ) { mfn = p2m->get_entry(p2m, gfn, &pt, &a, 0, NULL); if ( pt == ot ) - set_p2m_entry(p2m, gfn, mfn, PAGE_ORDER_4K, nt, p2m->default_access); + set_p2m_entry(p2m, gfn, mfn, PAGE_ORDER_4K, nt, + p2m->default_access); } - + p2m->defer_nested_flush = 0; + if ( nestedhvm_enabled(d) ) p2m_flush_nestedp2m(d); + p2m_unlock(p2m); } diff --git a/xen/arch/x86/mm/paging.c b/xen/arch/x86/mm/paging.c index ac9bb1a0ed..d59e8d6523 100644 --- a/xen/arch/x86/mm/paging.c +++ b/xen/arch/x86/mm/paging.c @@ -27,6 +27,7 @@ #include <asm/p2m.h> #include <asm/hap.h> #include <asm/hvm/nestedhvm.h> +#include <asm/dirty_vram.h> #include <xen/numa.h> #include <xsm/xsm.h> @@ -192,15 +193,11 @@ int paging_log_dirty_disable(struct domain *d) return ret; } -/* Mark a page as dirty */ +/* Given a guest mfn, mark a page as dirty */ void paging_mark_dirty(struct domain *d, unsigned long guest_mfn) { unsigned long pfn; mfn_t gmfn; - int changed; - mfn_t mfn, *l4, *l3, *l2; - unsigned long *l1; - int i1, i2, i3, i4; gmfn = _mfn(guest_mfn); @@ -210,6 +207,19 @@ void paging_mark_dirty(struct domain *d, unsigned long guest_mfn) /* We /really/ mean PFN here, even for non-translated guests. */ pfn = get_gpfn_from_mfn(mfn_x(gmfn)); + paging_mark_dirty_gpfn(d, pfn); +} + + +/* Given a guest pfn, mark a page as dirty */ +void paging_mark_dirty_gpfn(struct domain *d, unsigned long pfn) +{ + int changed; + mfn_t mfn, *l4, *l3, *l2; + unsigned long *l1; + int i1, i2, i3, i4; + dv_range_t *range; + /* Shared MFNs should NEVER be marked dirty */ BUG_ON(SHARED_M2P(pfn)); @@ -229,6 +239,11 @@ void paging_mark_dirty(struct domain *d, unsigned long guest_mfn) /* Recursive: this is called from inside the shadow code */ paging_lock_recursive(d); + d->arch.paging.log_dirty.dirty_count++; + range = dirty_vram_range_find_gfn(d, pfn); + if ( range ) + range->dirty_count++; + if ( unlikely(!mfn_valid(d->arch.paging.log_dirty.top)) ) { d->arch.paging.log_dirty.top = paging_new_log_dirty_node(d); @@ -445,7 +460,31 @@ void paging_log_dirty_range(struct domain *d, struct p2m_domain *p2m = p2m_get_hostp2m(d); int i; unsigned long pfn; + dv_range_t *range; + unsigned int range_dirty_count; + paging_lock(d); + range = dirty_vram_range_find_gfn(d, begin_pfn); + if ( !range ) + { + paging_unlock(d); + goto out; + } + + range_dirty_count = range->dirty_count; + range->dirty_count = 0; + + paging_unlock(d); + + if ( !range_dirty_count) + goto out; + + PAGING_DEBUG(LOGDIRTY, + "log-dirty-range: dom %u [%05lx:%05lx] range_dirty=%u\n", + d->domain_id, + begin_pfn, + begin_pfn + nr, + range_dirty_count); /* * Set l1e entries of P2M table to be read-only. * @@ -460,15 +499,17 @@ void paging_log_dirty_range(struct domain *d, for ( i = 0, pfn = begin_pfn; pfn < begin_pfn + nr; i++, pfn++ ) { - p2m_type_t pt; - pt = p2m_change_type(d, pfn, p2m_ram_rw, p2m_ram_logdirty); - if ( pt == p2m_ram_rw ) + if ( p2m_change_type(d, pfn, p2m_ram_rw, p2m_ram_logdirty) == + p2m_ram_rw ) dirty_bitmap[i >> 3] |= (1 << (i & 7)); } p2m_unlock(p2m); flush_tlb_mask(d->domain_dirty_cpumask); + + out: + return; } /* Note that this function takes three function pointers. Callers must supply diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index ce79131e1f..1e4b880187 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -36,6 +36,7 @@ #include <asm/current.h> #include <asm/flushtlb.h> #include <asm/shadow.h> +#include <asm/dirty_vram.h> #include <xen/numa.h> #include "private.h" @@ -3121,12 +3122,7 @@ void shadow_teardown(struct domain *d) * calls now that we've torn down the bitmap */ d->arch.paging.mode &= ~PG_log_dirty; - if (d->arch.hvm_domain.dirty_vram) { - xfree(d->arch.hvm_domain.dirty_vram->sl1ma); - xfree(d->arch.hvm_domain.dirty_vram->dirty_bitmap); - xfree(d->arch.hvm_domain.dirty_vram); - d->arch.hvm_domain.dirty_vram = NULL; - } + dirty_vram_free(d); paging_unlock(d); @@ -3464,178 +3460,217 @@ void shadow_clean_dirty_bitmap(struct domain *d) /**************************************************************************/ /* VRAM dirty tracking support */ -int shadow_track_dirty_vram(struct domain *d, - unsigned long begin_pfn, - unsigned long nr, - XEN_GUEST_HANDLE_64(uint8) dirty_bitmap) -{ - int rc; - unsigned long end_pfn = begin_pfn + nr; - unsigned long dirty_size = (nr + 7) / 8; - int flush_tlb = 0; - unsigned long i; - p2m_type_t t; - struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; - struct p2m_domain *p2m = p2m_get_hostp2m(d); - if (end_pfn < begin_pfn - || begin_pfn > p2m->max_mapped_pfn - || end_pfn >= p2m->max_mapped_pfn) - return -EINVAL; - /* We perform p2m lookups, so lock the p2m upfront to avoid deadlock */ - p2m_lock(p2m_get_hostp2m(d)); - paging_lock(d); +/* Support functions for shadow-based dirty VRAM code */ - if ( dirty_vram && (!nr || - ( begin_pfn != dirty_vram->begin_pfn - || end_pfn != dirty_vram->end_pfn )) ) - { - /* Different tracking, tear the previous down. */ - gdprintk(XENLOG_INFO, "stopping tracking VRAM %lx - %lx\n", dirty_vram->begin_pfn, dirty_vram->end_pfn); - xfree(dirty_vram->sl1ma); - xfree(dirty_vram->dirty_bitmap); - xfree(dirty_vram); - dirty_vram = d->arch.hvm_domain.dirty_vram = NULL; - } +#define DEBUG_unshadow_sl1ma 0 +#define DEBUG_unshadow_sl1ma_detail 0 +#define DEBUG_count_initial_mappings 0 - if ( !nr ) +/* smfn is no longer a shadow page. Remove it from any + * dirty vram range mapping. */ +void +dirty_vram_delete_shadow(struct vcpu *v, + unsigned long gfn, + unsigned int shadow_type, + mfn_t smfn) +{ + static unsigned int l1_shadow_mask = + 1 << SH_type_l1_32_shadow + | 1 << SH_type_fl1_32_shadow + | 1 << SH_type_l1_pae_shadow + | 1 << SH_type_fl1_pae_shadow + | 1 << SH_type_l1_64_shadow + | 1 << SH_type_fl1_64_shadow + ; + struct domain *d = v->domain; + dv_dirty_vram_t *dirty_vram; + struct list_head *curr, *next; + + ASSERT(paging_locked_by_me(d)); + /* Ignore all but level 1 shadows */ + + if ( (l1_shadow_mask & (1 << shadow_type)) == 0 ) { - rc = 0; goto out; } - /* This should happen seldomly (Video mode change), - * no need to be careful. */ + dirty_vram = d->arch.hvm_domain.dirty_vram; if ( !dirty_vram ) { - /* Throw away all the shadows rather than walking through them - * up to nr times getting rid of mappings of each pfn */ - shadow_blow_tables(d); - - gdprintk(XENLOG_INFO, "tracking VRAM %lx - %lx\n", begin_pfn, end_pfn); - - rc = -ENOMEM; - if ( (dirty_vram = xmalloc(struct sh_dirty_vram)) == NULL ) - goto out; - dirty_vram->begin_pfn = begin_pfn; - dirty_vram->end_pfn = end_pfn; - d->arch.hvm_domain.dirty_vram = dirty_vram; - - if ( (dirty_vram->sl1ma = xmalloc_array(paddr_t, nr)) == NULL ) - goto out_dirty_vram; - memset(dirty_vram->sl1ma, ~0, sizeof(paddr_t) * nr); - - if ( (dirty_vram->dirty_bitmap = xzalloc_array(uint8_t, dirty_size)) == NULL ) - goto out_sl1ma; - - dirty_vram->last_dirty = NOW(); - - /* Tell the caller that this time we could not track dirty bits. */ - rc = -ENODATA; - } - else if (dirty_vram->last_dirty == -1) - { - /* still completely clean, just copy our empty bitmap */ - rc = -EFAULT; - if ( copy_to_guest(dirty_bitmap, dirty_vram->dirty_bitmap, dirty_size) == 0 ) - rc = 0; + goto out; } - else + + list_for_each_safe(curr, next, &dirty_vram->range_head) { - /* Iterate over VRAM to track dirty bits. */ - for ( i = 0; i < nr; i++ ) { - mfn_t mfn = get_gfn_query_unlocked(d, begin_pfn + i, &t); - struct page_info *page; - int dirty = 0; - paddr_t sl1ma = dirty_vram->sl1ma[i]; - - if (mfn_x(mfn) == INVALID_MFN) - { - dirty = 1; - } - else + dv_range_t *range = list_entry(curr, dv_range_t, range_link); + unsigned long i; + int max_mappings = 1, mappings = 0; + int unshadowed = 0; + for ( i = 0; i != range->end_pfn - range->begin_pfn; i++ ) + { + dv_paddr_link_t *pl = &range->pl_tab[ i ].mapping; + dv_paddr_link_t **ppl = NULL; + mappings = 0; + + while ( pl != NULL ) { - page = mfn_to_page(mfn); - switch (page->u.inuse.type_info & PGT_count_mask) - { - case 0: - /* No guest reference, nothing to track. */ - break; - case 1: - /* One guest reference. */ - if ( sl1ma == INVALID_PADDR ) - { - /* We don't know which sl1e points to this, too bad. */ - dirty = 1; - /* TODO: Heuristics for finding the single mapping of - * this gmfn */ - flush_tlb |= sh_remove_all_mappings(d->vcpu[0], mfn); - } - else - { - /* Hopefully the most common case: only one mapping, - * whose dirty bit we can use. */ - l1_pgentry_t *sl1e = maddr_to_virt(sl1ma); - - if ( l1e_get_flags(*sl1e) & _PAGE_DIRTY ) - { - dirty = 1; - /* Note: this is atomic, so we may clear a - * _PAGE_ACCESSED set by another processor. */ - l1e_remove_flags(*sl1e, _PAGE_DIRTY); - flush_tlb = 1; - } - } - break; - default: - /* More than one guest reference, - * we don't afford tracking that. */ - dirty = 1; + paddr_t sl1ma = pl->sl1ma; + unsigned long sl1mn; + + if ( sl1ma == INVALID_PADDR ) break; + + sl1mn = sl1ma >> PAGE_SHIFT; + if ( sl1mn == mfn_x(smfn) ) { +#if DEBUG_unshadow_sl1ma_detail + gdprintk(XENLOG_DEBUG, + "[%lx] gfn[%lx] unshadow sl1ma:%lx\n", + mfn_x(smfn), + range->begin_pfn + i, + sl1ma); +#endif + unshadowed++; + pl = free_paddr_link(d, ppl, pl); + --range->nr_mappings; + } + else + { + ppl = &pl->pl_next; + pl = *ppl; + mappings++; } } - - if ( dirty ) + } + if ( mappings > max_mappings ) + max_mappings = mappings; + + if ( unshadowed ) { +#if DEBUG_unshadow_sl1ma + gdprintk(XENLOG_DEBUG, + "[%lx] gfn[%05lx:%05lx] unshadowed:%d mappings:0x%x " + "max_mappings:%d\n", + mfn_x(smfn), + range->begin_pfn, range->end_pfn, + unshadowed, range->nr_mappings, max_mappings); +#endif + if ( range->nr_mappings == 0 ) { - dirty_vram->dirty_bitmap[i / 8] |= 1 << (i % 8); - dirty_vram->last_dirty = NOW(); + dirty_vram_range_free(d, range); } } + } + out: + return; +} + - rc = -EFAULT; - if ( copy_to_guest(dirty_bitmap, dirty_vram->dirty_bitmap, dirty_size) == 0 ) { - memset(dirty_vram->dirty_bitmap, 0, dirty_size); - if (dirty_vram->last_dirty + SECONDS(2) < NOW()) +typedef int (*hash_pfn_callback_t)(struct vcpu *v, + mfn_t smfn, + unsigned long begin_pfn, + unsigned long end_pfn, + int *removed); + +static int hash_pfn_foreach(struct vcpu *v, + unsigned int callback_mask, + hash_pfn_callback_t callbacks[], + unsigned long begin_pfn, + unsigned long end_pfn) +/* Walk the hash table looking at the types of the entries and + * calling the appropriate callback function for each entry. + * The mask determines which shadow types we call back for, and the array + * of callbacks tells us which function to call. + * Any callback may return non-zero to let us skip the rest of the scan. + * + * WARNING: Callbacks MUST NOT add or remove hash entries unless they + * then return non-zero to terminate the scan. */ +{ + int i, done = 0, removed = 0; + struct domain *d = v->domain; + struct page_info *x; + + /* Say we're here, to stop hash-lookups reordering the chains */ + ASSERT(paging_locked_by_me(d)); + ASSERT(d->arch.paging.shadow.hash_walking == 0); + d->arch.paging.shadow.hash_walking = 1; + + for ( i = 0; i < SHADOW_HASH_BUCKETS; i++ ) + { + /* WARNING: This is not safe against changes to the hash table. + * The callback *must* return non-zero if it has inserted or + * deleted anything from the hash (lookups are OK, though). */ + for ( x = d->arch.paging.shadow.hash_table[i]; + x; + x = next_shadow(x) ) + { + if ( callback_mask & (1 << x->u.sh.type) ) { - /* was clean for more than two seconds, try to disable guest - * write access */ - for ( i = begin_pfn; i < end_pfn; i++ ) { - mfn_t mfn = get_gfn_query_unlocked(d, i, &t); - if (mfn_x(mfn) != INVALID_MFN) - flush_tlb |= sh_remove_write_access(d->vcpu[0], mfn, 1, 0); - } - dirty_vram->last_dirty = -1; + ASSERT(x->u.sh.type <= 15); + ASSERT(callbacks[x->u.sh.type] != NULL); + done = callbacks[x->u.sh.type](v, page_to_mfn(x), + begin_pfn, end_pfn, + &removed); + if ( done ) break; } - rc = 0; } + if ( done ) break; } - if ( flush_tlb ) - flush_tlb_mask(d->domain_dirty_cpumask); - goto out; + d->arch.paging.shadow.hash_walking = 0; + return removed; +} -out_sl1ma: - xfree(dirty_vram->sl1ma); -out_dirty_vram: - xfree(dirty_vram); - dirty_vram = d->arch.hvm_domain.dirty_vram = NULL; +void sh_find_all_vram_mappings(struct vcpu *v, + dv_range_t *range) +{ + /* Dispatch table for getting per-type functions */ + static hash_pfn_callback_t callbacks[SH_type_unused] = { + NULL, /* none */ + SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, 2), /* l1_32 */ + SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, 2), /* fl1_32 */ + NULL, /* l2_32 */ + SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, 3), /* l1_pae */ + SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, 3), /* fl1_pae */ + NULL, /* l2_pae */ + NULL, /* l2h_pae */ +#if CONFIG_PAGING_LEVELS >= 4 + SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, 4), /* l1_64 */ + SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, 4), /* fl1_64 */ +#else + NULL, /* l1_64 */ + NULL, /* fl1_64 */ +#endif + NULL, /* l2_64 */ + NULL, /* l2h_64 */ + NULL, /* l3_64 */ + NULL, /* l4_64 */ + NULL, /* p2m */ + NULL /* unused */ + }; -out: - paging_unlock(d); - p2m_unlock(p2m_get_hostp2m(d)); - return rc; + static unsigned int callback_mask = + 1 << SH_type_l1_32_shadow + | 1 << SH_type_fl1_32_shadow + | 1 << SH_type_l1_pae_shadow + | 1 << SH_type_fl1_pae_shadow + | 1 << SH_type_l1_64_shadow + | 1 << SH_type_fl1_64_shadow + ; + + perfc_incr(shadow_mappings); + + hash_pfn_foreach(v, callback_mask, callbacks, + range->begin_pfn, + range->end_pfn); + +#if DEBUG_count_initial_mappings + gdprintk(XENLOG_DEBUG, "[%05lx:%05lx] count of initial mappings:%d\n", + range->begin_pfn, range->end_pfn, + range->nr_mappings); +#endif } + /**************************************************************************/ /* Shadow-control XEN_DOMCTL dispatcher */ diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index 4967da1d35..bb983bcecc 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -35,6 +35,7 @@ #include <asm/flushtlb.h> #include <asm/hvm/hvm.h> #include <asm/hvm/cacheattr.h> +#include <asm/dirty_vram.h> #include <asm/mtrr.h> #include <asm/guest_pt.h> #include <public/sched.h> @@ -149,6 +150,10 @@ delete_fl1_shadow_status(struct vcpu *v, gfn_t gfn, mfn_t smfn) SHADOW_PRINTK("gfn=%"SH_PRI_gfn", type=%08x, smfn=%05lx\n", gfn_x(gfn), SH_type_fl1_shadow, mfn_x(smfn)); ASSERT(mfn_to_page(smfn)->u.sh.head); + + /* Removing any dv_paddr_links to the erstwhile shadow page */ + dirty_vram_delete_shadow(v, gfn_x(gfn), SH_type_fl1_shadow, smfn); + shadow_hash_delete(v, gfn_x(gfn), SH_type_fl1_shadow, smfn); } @@ -160,6 +165,10 @@ delete_shadow_status(struct vcpu *v, mfn_t gmfn, u32 shadow_type, mfn_t smfn) v->domain->domain_id, v->vcpu_id, mfn_x(gmfn), shadow_type, mfn_x(smfn)); ASSERT(mfn_to_page(smfn)->u.sh.head); + + /* Removing any dv_paddr_links to the erstwhile shadow page */ + dirty_vram_delete_shadow(v, mfn_x(gmfn), shadow_type, smfn); + shadow_hash_delete(v, mfn_x(gmfn), shadow_type, smfn); /* 32-on-64 PV guests don't own their l4 pages; see set_shadow_status */ if ( !is_pv_32on64_vcpu(v) || shadow_type != SH_type_l4_64_shadow ) @@ -516,7 +525,6 @@ _sh_propagate(struct vcpu *v, guest_l1e_t guest_entry = { guest_intpte }; shadow_l1e_t *sp = shadow_entry_ptr; struct domain *d = v->domain; - struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; gfn_t target_gfn = guest_l1e_get_gfn(guest_entry); u32 pass_thru_flags; u32 gflags, sflags; @@ -663,17 +671,6 @@ _sh_propagate(struct vcpu *v, } } - if ( unlikely((level == 1) && dirty_vram - && dirty_vram->last_dirty == -1 - && gfn_x(target_gfn) >= dirty_vram->begin_pfn - && gfn_x(target_gfn) < dirty_vram->end_pfn) ) - { - if ( ft & FETCH_TYPE_WRITE ) - dirty_vram->last_dirty = NOW(); - else - sflags &= ~_PAGE_RW; - } - /* Read-only memory */ if ( p2m_is_readonly(p2mt) || (p2mt == p2m_mmio_direct && @@ -1072,101 +1069,60 @@ static int shadow_set_l2e(struct vcpu *v, return flags; } -static inline void shadow_vram_get_l1e(shadow_l1e_t new_sl1e, +/* shadow_vram_fix_l1e() + * + * Tests L1PTEs as they are modified, looking for when they start to (or + * cease to) point to frame buffer pages. If the old and new gfns differ, + * calls dirty_vram_range_update() to updates the dirty_vram structures. + */ +static inline void shadow_vram_fix_l1e(shadow_l1e_t old_sl1e, + shadow_l1e_t new_sl1e, shadow_l1e_t *sl1e, mfn_t sl1mfn, struct domain *d) { - mfn_t mfn = shadow_l1e_get_mfn(new_sl1e); - int flags = shadow_l1e_get_flags(new_sl1e); - unsigned long gfn; - struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; + mfn_t new_mfn, old_mfn; + unsigned long new_gfn = INVALID_M2P_ENTRY, old_gfn = INVALID_M2P_ENTRY; + paddr_t sl1ma; + dv_dirty_vram_t *dirty_vram = d->arch.hvm_domain.dirty_vram; - if ( !dirty_vram /* tracking disabled? */ - || !(flags & _PAGE_RW) /* read-only mapping? */ - || !mfn_valid(mfn) ) /* mfn can be invalid in mmio_direct */ + if ( !dirty_vram ) return; - gfn = mfn_to_gfn(d, mfn); - /* Page sharing not supported on shadow PTs */ - BUG_ON(SHARED_M2P(gfn)); + sl1ma = pfn_to_paddr(mfn_x(sl1mfn)) | ((unsigned long)sl1e & ~PAGE_MASK); - if ( (gfn >= dirty_vram->begin_pfn) && (gfn < dirty_vram->end_pfn) ) + old_mfn = shadow_l1e_get_mfn(old_sl1e); + + if ( !sh_l1e_is_magic(old_sl1e) && + (l1e_get_flags(old_sl1e) & _PAGE_PRESENT) && + mfn_valid(old_mfn)) { - unsigned long i = gfn - dirty_vram->begin_pfn; - struct page_info *page = mfn_to_page(mfn); - - if ( (page->u.inuse.type_info & PGT_count_mask) == 1 ) - /* Initial guest reference, record it */ - dirty_vram->sl1ma[i] = pfn_to_paddr(mfn_x(sl1mfn)) - | ((unsigned long)sl1e & ~PAGE_MASK); + old_gfn = mfn_to_gfn(d, old_mfn); } -} - -static inline void shadow_vram_put_l1e(shadow_l1e_t old_sl1e, - shadow_l1e_t *sl1e, - mfn_t sl1mfn, - struct domain *d) -{ - mfn_t mfn = shadow_l1e_get_mfn(old_sl1e); - int flags = shadow_l1e_get_flags(old_sl1e); - unsigned long gfn; - struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; - - if ( !dirty_vram /* tracking disabled? */ - || !(flags & _PAGE_RW) /* read-only mapping? */ - || !mfn_valid(mfn) ) /* mfn can be invalid in mmio_direct */ - return; - - gfn = mfn_to_gfn(d, mfn); - /* Page sharing not supported on shadow PTs */ - BUG_ON(SHARED_M2P(gfn)); - - if ( (gfn >= dirty_vram->begin_pfn) && (gfn < dirty_vram->end_pfn) ) + + new_mfn = shadow_l1e_get_mfn(new_sl1e); + if ( !sh_l1e_is_magic(new_sl1e) && + (l1e_get_flags(new_sl1e) & _PAGE_PRESENT) && + mfn_valid(new_mfn)) { - unsigned long i = gfn - dirty_vram->begin_pfn; - struct page_info *page = mfn_to_page(mfn); - int dirty = 0; - paddr_t sl1ma = pfn_to_paddr(mfn_x(sl1mfn)) - | ((unsigned long)sl1e & ~PAGE_MASK); + new_gfn = mfn_to_gfn(d, new_mfn); + } - if ( (page->u.inuse.type_info & PGT_count_mask) == 1 ) - { - /* Last reference */ - if ( dirty_vram->sl1ma[i] == INVALID_PADDR ) { - /* We didn't know it was that one, let's say it is dirty */ - dirty = 1; - } - else - { - ASSERT(dirty_vram->sl1ma[i] == sl1ma); - dirty_vram->sl1ma[i] = INVALID_PADDR; - if ( flags & _PAGE_DIRTY ) - dirty = 1; - } - } - else + if ( old_gfn == new_gfn ) return; + + if ( VALID_M2P(old_gfn) ) + if ( dirty_vram_range_update(d, old_gfn, sl1ma, 0/*clear*/) ) { - /* We had more than one reference, just consider the page dirty. */ - dirty = 1; - /* Check that it's not the one we recorded. */ - if ( dirty_vram->sl1ma[i] == sl1ma ) - { - /* Too bad, we remembered the wrong one... */ - dirty_vram->sl1ma[i] = INVALID_PADDR; - } - else - { - /* Ok, our recorded sl1e is still pointing to this page, let's - * just hope it will remain. */ - } + SHADOW_PRINTK("gfn %lx (mfn %lx) cleared vram pte\n", + old_gfn, mfn_x(old_mfn)); } - if ( dirty ) + + if ( VALID_M2P(new_gfn) ) + if ( dirty_vram_range_update(d, new_gfn, sl1ma, 1/*set*/) ) { - dirty_vram->dirty_bitmap[i / 8] |= 1 << (i % 8); - dirty_vram->last_dirty = NOW(); + SHADOW_PRINTK("gfn %lx (mfn %lx) set vram pte\n", + new_gfn, mfn_x(new_mfn)); } - } } static int shadow_set_l1e(struct vcpu *v, @@ -1211,12 +1167,13 @@ static int shadow_set_l1e(struct vcpu *v, shadow_l1e_remove_flags(new_sl1e, _PAGE_RW); /* fall through */ case 0: - shadow_vram_get_l1e(new_sl1e, sl1e, sl1mfn, d); break; } } } + shadow_vram_fix_l1e(old_sl1e, new_sl1e, sl1e, sl1mfn, d); + /* Write the new entry */ shadow_write_entries(sl1e, &new_sl1e, 1, sl1mfn); flags |= SHADOW_SET_CHANGED; @@ -1231,7 +1188,6 @@ static int shadow_set_l1e(struct vcpu *v, * trigger a flush later. */ if ( shadow_mode_refcounts(d) ) { - shadow_vram_put_l1e(old_sl1e, sl1e, sl1mfn, d); shadow_put_page_from_l1e(old_sl1e, d); TRACE_SHADOW_PATH_FLAG(TRCE_SFLAG_SHADOW_L1_PUT_REF); } @@ -2018,7 +1974,6 @@ void sh_destroy_l1_shadow(struct vcpu *v, mfn_t smfn) SHADOW_FOREACH_L1E(sl1mfn, sl1e, 0, 0, { if ( (shadow_l1e_get_flags(*sl1e) & _PAGE_PRESENT) && !sh_l1e_is_magic(*sl1e) ) { - shadow_vram_put_l1e(*sl1e, sl1e, sl1mfn, d); shadow_put_page_from_l1e(*sl1e, d); } }); @@ -4336,6 +4291,37 @@ int sh_rm_mappings_from_l1(struct vcpu *v, mfn_t sl1mfn, mfn_t target_mfn) return done; } + +int sh_find_vram_mappings_in_l1(struct vcpu *v, + mfn_t sl1mfn, + unsigned long begin_pfn, + unsigned long end_pfn, + int *removed) +/* Find all VRAM mappings in this shadow l1 table */ +{ + struct domain *d = v->domain; + shadow_l1e_t *sl1e; + int done = 0; + + /* only returns _PAGE_PRESENT entries */ + SHADOW_FOREACH_L1E(sl1mfn, sl1e, 0, done, + { + unsigned long gfn; + mfn_t gmfn = shadow_l1e_get_mfn(*sl1e); + if ( !mfn_valid(gmfn) ) + continue; + gfn = mfn_to_gfn(d, gmfn); + if ( VALID_M2P(gfn) && (begin_pfn <= gfn) && (gfn < end_pfn) ) + { + paddr_t sl1ma = + pfn_to_paddr(mfn_x(sl1mfn)) | + ( (unsigned long)sl1e & ~PAGE_MASK ); + dirty_vram_range_update(v->domain, gfn, sl1ma, 1/*set*/); + } + }); + return 0; +} + /**************************************************************************/ /* Functions to excise all pointers to shadows from higher-level shadows. */ diff --git a/xen/arch/x86/mm/shadow/multi.h b/xen/arch/x86/mm/shadow/multi.h index 835121e494..436a4ace8a 100644 --- a/xen/arch/x86/mm/shadow/multi.h +++ b/xen/arch/x86/mm/shadow/multi.h @@ -66,7 +66,12 @@ SHADOW_INTERNAL_NAME(sh_rm_write_access_from_l1, GUEST_LEVELS) extern int SHADOW_INTERNAL_NAME(sh_rm_mappings_from_l1, GUEST_LEVELS) (struct vcpu *v, mfn_t sl1mfn, mfn_t target_mfn); - +extern int +SHADOW_INTERNAL_NAME(sh_find_vram_mappings_in_l1, GUEST_LEVELS) + (struct vcpu *v, mfn_t sl1mfn, + unsigned long begin_pfn, + unsigned long end_pfn, + int *removed); extern void SHADOW_INTERNAL_NAME(sh_clear_shadow_entry, GUEST_LEVELS) (struct vcpu *v, void *ep, mfn_t smfn); diff --git a/xen/arch/x86/mm/shadow/types.h b/xen/arch/x86/mm/shadow/types.h index 43ce1dbb0a..5b0f9f7644 100644 --- a/xen/arch/x86/mm/shadow/types.h +++ b/xen/arch/x86/mm/shadow/types.h @@ -229,6 +229,7 @@ static inline shadow_l4e_t shadow_l4e_from_mfn(mfn_t mfn, u32 flags) #define sh_update_cr3 INTERNAL_NAME(sh_update_cr3) #define sh_rm_write_access_from_l1 INTERNAL_NAME(sh_rm_write_access_from_l1) #define sh_rm_mappings_from_l1 INTERNAL_NAME(sh_rm_mappings_from_l1) +#define sh_find_vram_mappings_in_l1 INTERNAL_NAME(sh_find_vram_mappings_in_l1) #define sh_remove_l1_shadow INTERNAL_NAME(sh_remove_l1_shadow) #define sh_remove_l2_shadow INTERNAL_NAME(sh_remove_l2_shadow) #define sh_remove_l3_shadow INTERNAL_NAME(sh_remove_l3_shadow) diff --git a/xen/include/asm-x86/dirty_vram.h b/xen/include/asm-x86/dirty_vram.h new file mode 100644 index 0000000000..acdd2c4c8c --- /dev/null +++ b/xen/include/asm-x86/dirty_vram.h @@ -0,0 +1,202 @@ +/**************************************************************************** + * include/asm-x86/dirty_vram.h + * + * Interface for tracking dirty VRAM pages + * + * Copyright (c) 2012 Citrix Systems, Inc. (Robert Phillips) + * Parts of this code are Copyright (c) 2007 Advanced Micro Devices (Wei Huang) + * Parts of this code are Copyright (c) 2006 by XenSource Inc. + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DIRTY_VRAM_H +#define _DIRTY_VRAM_H + +/* + * In shadow mode we need to bookkeep all the L1 page table entries that + * map a frame buffer page. Struct dv_paddr_link does this by + * recording the address of a L1 page table entry for some frame buffer page. + * Also has a link to additional pl entries if the frame buffer page + * has multiple mappings. + * In practice very few pages have multiple mappings. + * But to rule out some pathological situation, we limit the number of + * mappings we're willing to bookkeep. + */ + +#define DV_ADDR_LINK_LIST_LIMIT 64 + +typedef struct dv_paddr_link { + paddr_t sl1ma; + struct dv_paddr_link *pl_next; +} dv_paddr_link_t; + +typedef struct dv_pl_entry { + dv_paddr_link_t mapping; + bool_t stuck_dirty; +} dv_pl_entry_t; + +/* + * This defines an extension page of pl entries for FB pages with multiple + * mappings. All such pages (of a domain) are linked together. + */ +typedef struct dv_paddr_link_ext { + struct list_head ext_link; + dv_paddr_link_t entries[ ( PAGE_SIZE - sizeof( struct list_head ) ) / + sizeof( dv_paddr_link_t ) ]; +} dv_paddr_link_ext_t; + +/* + * This defines a single frame buffer range. It bookkeeps all the + * level 1 PTEs that map guest pages within that range. + * All such ranges (of a domain) are linked together. + */ +typedef struct dv_range { + struct list_head range_link; /* the several ranges form a linked list */ + unsigned long begin_pfn; + unsigned long end_pfn; + dv_pl_entry_t *pl_tab; /* table has 1 pl entry per pfn in range */ + int nr_mappings; /* total number of mappings in this range */ + int mappings_hwm; /* high water mark of max mapping count */ + unsigned int dirty_count; +} dv_range_t; + +/* + * This contains all the data structures required by a domain to + * bookkeep the dirty pages within its frame buffers. + */ +typedef struct dv_dirty_vram { + struct list_head range_head; /* head of the linked list of ranges */ + struct list_head ext_head; /* head of list of extension pages */ + dv_paddr_link_t *pl_free; /* free list of pl's within extension pages */ + int nr_ranges; /* bookkeeps number of ranges */ + int ranges_hwm; /* high water mark of max number of ranges */ +} dv_dirty_vram_t; + +/* Allocates domain's dirty_vram structure */ +dv_dirty_vram_t * +dirty_vram_alloc(struct domain *d); + +/* + * Returns domain's dirty_vram structure, + * allocating it if necessary + */ +dv_dirty_vram_t * +dirty_vram_find_or_alloc(struct domain *d); + +/* Frees domain's dirty_vram structure */ +void dirty_vram_free(struct domain *d); + +/* Returns dirty vram range containing gfn, NULL if none */ +struct dv_range * +dirty_vram_range_find_gfn(struct domain *d, + unsigned long gfn); + +/* + * Returns dirty vram range matching [ begin_pfn .. begin_pfn+nr ), + * NULL if none + */ +dv_range_t * +dirty_vram_range_find(struct domain *d, + unsigned long begin_pfn, + unsigned long nr); + +/* + * Allocate dirty vram range containing [ begin_pfn .. begin_pfn+nr ), + * freeing any existing range that overlaps the new range. + */ +dv_range_t * +dirty_vram_range_alloc(struct domain *d, + unsigned long begin_pfn, + unsigned long nr); + +/* + * Returns dirty vram range matching [ begin_pfn .. begin_pfn+nr ), + * creating a range if none already exists and + * freeing any existing range that overlaps the new range. + */ +dv_range_t * +dirty_vram_range_find_or_alloc(struct domain *d, + unsigned long begin_pfn, + unsigned long nr); + +void dirty_vram_range_free(struct domain *d, + dv_range_t *range); + +/* Bookkeep PTE address of a frame buffer page */ +int dirty_vram_range_update(struct domain *d, + unsigned long gfn, + paddr_t sl1ma, + int set); + +/* + * smfn is no longer a shadow page. Remove it from any + * dirty vram range mapping. + */ +void +dirty_vram_delete_shadow(struct vcpu *v, + unsigned long gfn, + unsigned int shadow_type, + mfn_t smfn); + + +/* + * Scan all the L1 tables looking for VRAM mappings. + * Record them in the domain's dv_dirty_vram structure + */ +void sh_find_all_vram_mappings(struct vcpu *v, + dv_range_t *range); + +/* + * Free a paddr_link struct, given address of its + * predecessor in singly-linked list + */ +dv_paddr_link_t * +free_paddr_link(struct domain *d, + dv_paddr_link_t **ppl, + dv_paddr_link_t *pl); + + +/* Enable VRAM dirty tracking. */ +int +shadow_track_dirty_vram(struct domain *d, + unsigned long first_pfn, + unsigned long nr, + XEN_GUEST_HANDLE_64(uint8) dirty_bitmap); + +int +hap_track_dirty_vram(struct domain *d, + unsigned long begin_pfn, + unsigned long nr, + XEN_GUEST_HANDLE_64(uint8) dirty_bitmap); + +void +hap_clean_vram_tracking_range(struct domain *d, + unsigned long begin_pfn, + unsigned long nr, + uint8_t *dirty_bitmap); + +#endif /* _DIRTY_VRAM_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/hap.h b/xen/include/asm-x86/hap.h index 916a35bdc7..3e3a1f5537 100644 --- a/xen/include/asm-x86/hap.h +++ b/xen/include/asm-x86/hap.h @@ -57,10 +57,6 @@ void hap_final_teardown(struct domain *d); void hap_teardown(struct domain *d); void hap_vcpu_init(struct vcpu *v); void hap_logdirty_init(struct domain *d); -int hap_track_dirty_vram(struct domain *d, - unsigned long begin_pfn, - unsigned long nr, - XEN_GUEST_HANDLE_64(uint8) dirty_bitmap); extern const struct paging_mode *hap_paging_get_mode(struct vcpu *); diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h index 27b3de50dc..0cc7b05b17 100644 --- a/xen/include/asm-x86/hvm/domain.h +++ b/xen/include/asm-x86/hvm/domain.h @@ -74,7 +74,7 @@ struct hvm_domain { struct list_head pinned_cacheattr_ranges; /* VRAM dirty support. */ - struct sh_dirty_vram *dirty_vram; + struct dv_dirty_vram *dirty_vram; /* If one of vcpus of this domain is in no_fill_mode or * mtrr/pat between vcpus is not the same, set is_in_uc_mode diff --git a/xen/include/asm-x86/paging.h b/xen/include/asm-x86/paging.h index c3a8848a56..e22df388ba 100644 --- a/xen/include/asm-x86/paging.h +++ b/xen/include/asm-x86/paging.h @@ -154,9 +154,13 @@ void paging_log_dirty_init(struct domain *d, int (*disable_log_dirty)(struct domain *d), void (*clean_dirty_bitmap)(struct domain *d)); -/* mark a page as dirty */ +/* mark a gmfn as dirty, a wrapper around marking a gpfn as dirty */ void paging_mark_dirty(struct domain *d, unsigned long guest_mfn); +/* mark a gpfn as dirty */ +void paging_mark_dirty_gpfn(struct domain *d, unsigned long gpfn); + + /* is this guest page dirty? * This is called from inside paging code, with the paging lock held. */ int paging_mfn_is_dirty(struct domain *d, mfn_t gmfn); @@ -183,15 +187,6 @@ int paging_mfn_is_dirty(struct domain *d, mfn_t gmfn); #define L4_LOGDIRTY_IDX(pfn) 0 #endif -/* VRAM dirty tracking support */ -struct sh_dirty_vram { - unsigned long begin_pfn; - unsigned long end_pfn; - paddr_t *sl1ma; - uint8_t *dirty_bitmap; - s_time_t last_dirty; -}; - /***************************************************************************** * Entry points into the paging-assistance code */ diff --git a/xen/include/asm-x86/shadow.h b/xen/include/asm-x86/shadow.h index 2eb6efc18e..940d7fde18 100644 --- a/xen/include/asm-x86/shadow.h +++ b/xen/include/asm-x86/shadow.h @@ -62,12 +62,6 @@ void shadow_vcpu_init(struct vcpu *v); /* Enable an arbitrary shadow mode. Call once at domain creation. */ int shadow_enable(struct domain *d, u32 mode); -/* Enable VRAM dirty bit tracking. */ -int shadow_track_dirty_vram(struct domain *d, - unsigned long first_pfn, - unsigned long nr, - XEN_GUEST_HANDLE_64(uint8) dirty_bitmap); - /* Handler for shadow control ops: operations from user-space to enable * and disable ephemeral shadow modes (test mode and log-dirty mode) and * manipulate the log-dirty bitmap. */ |