# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1278314846 -3600
# Node ID b7e2bb28bfb7c60a62e9ed4a9c3c705e8038b603
# Parent 9074d50d09358cd8349d54c7ab2e2ead81fa1570
VMX: fix ept pages free up when ept superpage split fails:
1) implement ept super page split in a recursive way to
form an ept sub tree before real installation;
2) free an ept sub tree also in a recursive way.
3) change ept_next_level last input parameter from shift
bits # to next walk level;
Signed-off-by: Xin Li <xin.li@xxxxxxxxx>
---
xen/arch/x86/mm/hap/p2m-ept.c | 231 +++++++++++++++++++++++-------------------
1 files changed, 129 insertions(+), 102 deletions(-)
diff -r 9074d50d0935 -r b7e2bb28bfb7 xen/arch/x86/mm/hap/p2m-ept.c
--- a/xen/arch/x86/mm/hap/p2m-ept.c Mon Jul 05 08:24:18 2010 +0100
+++ b/xen/arch/x86/mm/hap/p2m-ept.c Mon Jul 05 08:27:26 2010 +0100
@@ -118,6 +118,74 @@ static int ept_set_middle_entry(struct d
return 1;
}
+/* free ept sub tree behind an entry */
+void ept_free_entry(struct domain *d, ept_entry_t *ept_entry, int level)
+{
+ /* End if the entry is a leaf entry. */
+ if ( level == 0 || !is_epte_present(ept_entry) ||
+ is_epte_superpage(ept_entry) )
+ return;
+
+ if ( level > 1 )
+ {
+ ept_entry_t *epte = map_domain_page(ept_entry->mfn);
+ for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ )
+ ept_free_entry(d, epte + i, level - 1);
+ unmap_domain_page(epte);
+ }
+
+ d->arch.p2m->free_page(d, mfn_to_page(ept_entry->mfn));
+}
+
+static int ept_split_super_page(struct domain *d, ept_entry_t *ept_entry,
+ int level, int target)
+{
+ ept_entry_t new_ept, *table;
+ uint64_t trunk;
+ int rv = 1;
+
+ /* End if the entry is a leaf entry or reaches the target level. */
+ if ( level == 0 || level == target )
+ return rv;
+
+ ASSERT(is_epte_superpage(ept_entry));
+
+ if ( !ept_set_middle_entry(d, &new_ept) )
+ return 0;
+
+ table = map_domain_page(new_ept.mfn);
+ trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER);
+
+ for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ )
+ {
+ ept_entry_t *epte = table + i;
+
+ epte->emt = ept_entry->emt;
+ epte->ipat = ept_entry->ipat;
+ epte->sp = (level > 1) ? 1 : 0;
+ epte->avail1 = ept_entry->avail1;
+ epte->avail2 = 0;
+ epte->mfn = ept_entry->mfn + i * trunk;
+
+ ept_p2m_type_to_flags(epte, epte->avail1);
+
+ if ( (level - 1) == target )
+ continue;
+
+ ASSERT(is_epte_superpage(epte));
+
+ if ( !(rv = ept_split_super_page(d, epte, level - 1, target)) )
+ break;
+ }
+
+ unmap_domain_page(table);
+
+ /* Even failed we should install the newly allocated ept page. */
+ *ept_entry = new_ept;
+
+ return rv;
+}
+
/* Take the currently mapped table, find the corresponding gfn entry,
* and map the next table, if available. If the entry is empty
* and read_only is set,
@@ -134,13 +202,18 @@ static int ept_set_middle_entry(struct d
*/
static int ept_next_level(struct domain *d, bool_t read_only,
ept_entry_t **table, unsigned long *gfn_remainder,
- u32 shift)
-{
+ int next_level)
+{
+ unsigned long mfn;
ept_entry_t *ept_entry;
- ept_entry_t *next;
- u32 index;
+ u32 shift, index;
+
+ shift = next_level * EPT_TABLE_ORDER;
index = *gfn_remainder >> shift;
+
+ /* index must be falling into the page */
+ ASSERT(index < EPT_PAGETABLE_ENTRIES);
ept_entry = (*table) + index;
@@ -159,69 +232,12 @@ static int ept_next_level(struct domain
/* The only time sp would be set here is if we had hit a superpage */
if ( is_epte_superpage(ept_entry) )
return GUEST_TABLE_SUPER_PAGE;
- else
- {
- *gfn_remainder &= (1UL << shift) - 1;
- next = map_domain_page(ept_entry->mfn);
- unmap_domain_page(*table);
- *table = next;
- return GUEST_TABLE_NORMAL_PAGE;
- }
-}
-
-/* It's super page before and we should break down it now. */
-static int ept_split_large_page(struct domain *d,
- ept_entry_t **table, u32 *index,
- unsigned long gfn, int level)
-{
- ept_entry_t *prev_table = *table;
- ept_entry_t *split_table = NULL;
- ept_entry_t *split_entry = NULL;
- ept_entry_t *ept_entry = (*table) + (*index);
- ept_entry_t temp_ept_entry;
- unsigned long s_gfn, s_mfn;
- unsigned long offset, trunk;
- int i;
-
- /* alloc new page for new ept middle level entry which is
- * before a leaf super entry
- */
-
- if ( !ept_set_middle_entry(d, &temp_ept_entry) )
- return 0;
-
- /* split the super page to small next level pages */
- split_table = map_domain_page(temp_ept_entry.mfn);
- offset = gfn & ((1UL << (level * EPT_TABLE_ORDER)) - 1);
- trunk = (1UL << ((level-1) * EPT_TABLE_ORDER));
-
- for ( i = 0; i < (1UL << EPT_TABLE_ORDER); i++ )
- {
- s_gfn = gfn - offset + i * trunk;
- s_mfn = ept_entry->mfn + i * trunk;
-
- split_entry = split_table + i;
- split_entry->emt = ept_entry->emt;
- split_entry->ipat = ept_entry->ipat;
-
- split_entry->sp = (level > 1) ? 1 : 0;
-
- split_entry->mfn = s_mfn;
-
- split_entry->avail1 = ept_entry->avail1;
- split_entry->avail2 = 0;
- /* last step */
- split_entry->r = split_entry->w = split_entry->x = 1;
- ept_p2m_type_to_flags(split_entry, ept_entry->avail1);
- }
-
- *ept_entry = temp_ept_entry;
-
- *index = offset / trunk;
- *table = split_table;
- unmap_domain_page(prev_table);
-
- return 1;
+
+ mfn = ept_entry->mfn;
+ unmap_domain_page(*table);
+ *table = map_domain_page(mfn);
+ *gfn_remainder &= (1UL << shift) - 1;
+ return GUEST_TABLE_NORMAL_PAGE;
}
/*
@@ -265,7 +281,7 @@ ept_set_entry(struct domain *d, unsigned
for ( i = ept_get_wl(d); i > target; i-- )
{
- ret = ept_next_level(d, 0, &table, &gfn_remainder, i *
EPT_TABLE_ORDER);
+ ret = ept_next_level(d, 0, &table, &gfn_remainder, i);
if ( !ret )
goto out;
else if ( ret != GUEST_TABLE_NORMAL_PAGE )
@@ -275,11 +291,9 @@ ept_set_entry(struct domain *d, unsigned
ASSERT(ret != GUEST_TABLE_POD_PAGE || i != target);
index = gfn_remainder >> (i * EPT_TABLE_ORDER);
- gfn_remainder &= (1UL << (i * EPT_TABLE_ORDER)) - 1;
+ offset = gfn_remainder & ((1UL << (i * EPT_TABLE_ORDER)) - 1);
ept_entry = table + index;
-
- offset = gfn_remainder;
/*
* When we are here, we must be on a leaf ept entry
@@ -301,15 +315,14 @@ ept_set_entry(struct domain *d, unsigned
direct_mmio);
ept_entry->ipat = ipat;
ept_entry->sp = order ? 1 : 0;
+ ept_entry->avail1 = p2mt;
+ ept_entry->avail2 = 0;
if ( ept_entry->mfn == mfn_x(mfn) )
need_modify_vtd_table = 0;
else
ept_entry->mfn = mfn_x(mfn);
- ept_entry->avail1 = p2mt;
- ept_entry->avail2 = 0;
-
ept_p2m_type_to_flags(ept_entry, p2mt);
}
else
@@ -318,33 +331,50 @@ ept_set_entry(struct domain *d, unsigned
else
{
/* We need to split the original page. */
- ept_entry_t *split_ept_entry;
+ ept_entry_t split_ept_entry;
ASSERT(is_epte_superpage(ept_entry));
+ split_ept_entry = *ept_entry;
+
+ if ( !ept_split_super_page(d, &split_ept_entry, i, target) )
+ {
+ ept_free_entry(d, &split_ept_entry, i);
+ goto out;
+ }
+
+ /* now install the newly split ept sub-tree */
+ /* NB: please make sure domian is paused and no in-fly VT-d DMA. */
+ *ept_entry = split_ept_entry;
+
+ /* then move to the level we want to make real changes */
for ( ; i > target; i-- )
- {
- rv = ept_split_large_page(d, &table, &index, gfn, i);
- if ( !rv )
- goto out;
- }
-
- split_ept_entry = table + index;
- split_ept_entry->avail1 = p2mt;
- ept_p2m_type_to_flags(split_ept_entry, p2mt);
- split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn, &ipat,
- direct_mmio);
- split_ept_entry->ipat = ipat;
-
- if ( split_ept_entry->mfn == mfn_x(mfn) )
- need_modify_vtd_table = 0;
- else
- split_ept_entry->mfn = mfn_x(mfn);
+ ept_next_level(d, 0, &table, &gfn_remainder, i);
+
+ ASSERT(i == target);
+
+ index = gfn_remainder >> (i * EPT_TABLE_ORDER);
+ offset = gfn_remainder & ((1UL << (i * EPT_TABLE_ORDER)) - 1);
+
+ ept_entry = table + index;
+
+ ept_entry->emt = epte_get_entry_emt(d, gfn, mfn, &ipat, direct_mmio);
+ ept_entry->ipat = ipat;
+ ept_entry->sp = i ? 1 : 0;
+ ept_entry->avail1 = p2mt;
+ ept_entry->avail2 = 0;
+
+ if ( ept_entry->mfn == mfn_x(mfn) )
+ need_modify_vtd_table = 0;
+ else /* the caller should take care of the previous page */
+ ept_entry->mfn = mfn_x(mfn);
+
+ ept_p2m_type_to_flags(ept_entry, p2mt);
}
/* Track the highest gfn for which we have ever had a valid mapping */
- if ( mfn_valid(mfn_x(mfn))
- && (gfn + (1UL << order) - 1 > d->arch.p2m->max_mapped_pfn) )
+ if ( mfn_valid(mfn_x(mfn)) &&
+ (gfn + (1UL << order) - 1 > d->arch.p2m->max_mapped_pfn) )
d->arch.p2m->max_mapped_pfn = gfn + (1UL << order) - 1;
/* Success */
@@ -366,11 +396,11 @@ out:
for ( i = 0; i < (1 << order); i++ )
iommu_map_page(
d, gfn - offset + i, mfn_x(mfn) - offset + i,
- IOMMUF_readable|IOMMUF_writable);
+ IOMMUF_readable | IOMMUF_writable);
}
else if ( !order )
iommu_map_page(
- d, gfn, mfn_x(mfn), IOMMUF_readable|IOMMUF_writable);
+ d, gfn, mfn_x(mfn), IOMMUF_readable | IOMMUF_writable);
}
else
{
@@ -410,8 +440,7 @@ static mfn_t ept_get_entry(struct domain
for ( i = ept_get_wl(d); i > 0; i-- )
{
retry:
- ret = ept_next_level(d, 1, &table, &gfn_remainder,
- i * EPT_TABLE_ORDER);
+ ret = ept_next_level(d, 1, &table, &gfn_remainder, i);
if ( !ret )
goto out;
else if ( ret == GUEST_TABLE_POD_PAGE )
@@ -498,8 +527,7 @@ static ept_entry_t ept_get_entry_content
for ( i = ept_get_wl(d); i > 0; i-- )
{
- ret = ept_next_level(d, 1, &table, &gfn_remainder,
- i * EPT_TABLE_ORDER);
+ ret = ept_next_level(d, 1, &table, &gfn_remainder, i);
if ( !ret || ret == GUEST_TABLE_POD_PAGE )
goto out;
else if ( ret == GUEST_TABLE_SUPER_PAGE )
@@ -722,8 +750,7 @@ static void ept_dump_p2m_table(unsigned
for ( i = ept_get_wl(d); i > 0; i-- )
{
- ret = ept_next_level(d, 1, &table, &gfn_remainder,
- i * EPT_TABLE_ORDER);
+ ret = ept_next_level(d, 1, &table, &gfn_remainder, i);
if ( ret != GUEST_TABLE_NORMAL_PAGE )
break;
}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|