mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
x86/mm: remove two files left over from the previous vram patches.
I seem to have missed these when reverting 26399:b0e618cb0233. Signed-off-by: Tim Deegan <tim@xen.org> Committed-by: Tim Deegan <tim@xen.org>
This commit is contained in:
1 parent
ec13fd3b0e
commit
08faebd10a
2 files changed
-1066
No files matched your search
@@ -1,864 +0,0 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
||||
@@ -1,202 +0,0 @@
|
||||
/****************************************************************************
|
||||
* 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:
|
||||
*/
|
||||