aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir@xen.org>2010-12-15 10:47:52 +0000
committerKeir Fraser <keir@xen.org>2010-12-15 10:47:52 +0000
commitbb75b3bfdefd083a88cf2c041e4a7c0c8cc2777d (patch)
treec39c2a690e3d53e8b22a3a1c91b0eb9a9bbb2483
parent0f496f4bb7316325026083db31591ec46e1c4d0a (diff)
downloadxen-bb75b3bfdefd083a88cf2c041e4a7c0c8cc2777d.tar.gz
xen-bb75b3bfdefd083a88cf2c041e4a7c0c8cc2777d.tar.bz2
xen-bb75b3bfdefd083a88cf2c041e4a7c0c8cc2777d.zip
ept: Remove lock in ept_get_entry, replace with access-once semantics.
This mirrors the RVI/shadow situation, where p2m read access is lockless because it's done in the hardware (linear map of the p2m table). This fixes the original bug (call it bug A) without introducing bug B (a deadlock). Bug A was caused by a race when updating p2m entries: between testing if it's valid, and testing if it's populate-on-demand, it may have been changed from populate-on-demand to valid. My original patch simply introduced a lock into ept_get_entry, but that caused bug B, caused by circular locking order: p2m_change_type [grabs p2m lock] -> set_p2m_entry -> ept_set_entry -> ept_set_middle_level -> p2m_alloc [grabs hap lock] write cr4 -> hap_update_paging_modes [grabes hap lock] -> hap_update_cr3 -> gfn_to_mfn -> ept_get_entry -> [grabs p2m lock] Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com> xen-unstable changeset: 22526:7a5ee3800417 xen-unstable date: Wed Dec 15 10:47:05 2010 +0000
-rw-r--r--xen/arch/x86/mm/hap/p2m-ept.c40
1 files changed, 24 insertions, 16 deletions
diff --git a/xen/arch/x86/mm/hap/p2m-ept.c b/xen/arch/x86/mm/hap/p2m-ept.c
index b899b8b44c..50a346231a 100644
--- a/xen/arch/x86/mm/hap/p2m-ept.c
+++ b/xen/arch/x86/mm/hap/p2m-ept.c
@@ -137,7 +137,7 @@ static int ept_next_level(struct domain *d, bool_t read_only,
ept_entry_t **table, unsigned long *gfn_remainder,
u32 shift)
{
- ept_entry_t *ept_entry;
+ ept_entry_t *ept_entry, e;
ept_entry_t *next;
u32 index;
@@ -145,9 +145,11 @@ static int ept_next_level(struct domain *d, bool_t read_only,
ept_entry = (*table) + index;
- if ( !is_epte_present(ept_entry) )
+ e=*ept_entry;
+
+ if ( !is_epte_present(&e) )
{
- if ( ept_entry->avail1 == p2m_populate_on_demand )
+ if ( e.avail1 == p2m_populate_on_demand )
return GUEST_TABLE_POD_PAGE;
if ( read_only )
@@ -155,15 +157,17 @@ static int ept_next_level(struct domain *d, bool_t read_only,
if ( !ept_set_middle_entry(d, ept_entry) )
return GUEST_TABLE_MAP_FAILED;
+ else
+ e=*ept_entry;
}
/* The only time sp would be set here is if we had hit a superpage */
- if ( is_epte_superpage(ept_entry) )
+ if ( is_epte_superpage(&e) )
return GUEST_TABLE_SUPER_PAGE;
else
{
*gfn_remainder &= (1UL << shift) - 1;
- next = map_domain_page(ept_entry->mfn);
+ next = map_domain_page(e.mfn);
unmap_domain_page(*table);
*table = next;
return GUEST_TABLE_NORMAL_PAGE;
@@ -235,35 +239,39 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
if ( mfn_valid(mfn_x(mfn)) || direct_mmio || p2m_is_paged(p2mt) ||
(p2mt == p2m_ram_paging_in_start) )
{
- ept_entry->emt = epte_get_entry_emt(d, gfn, mfn, &ipat,
+ ept_entry_t new_entry;
+
+ new_entry.emt = epte_get_entry_emt(d, gfn, mfn, &ipat,
direct_mmio);
- ept_entry->ipat = ipat;
- ept_entry->sp = order ? 1 : 0;
+ new_entry.ipat = ipat;
+ new_entry.sp = order ? 1 : 0;
if ( ret == GUEST_TABLE_SUPER_PAGE )
{
- if ( ept_entry->mfn == (mfn_x(mfn) - offset) )
+ if ( new_entry.mfn == (mfn_x(mfn) - offset) )
need_modify_vtd_table = 0;
else
- ept_entry->mfn = mfn_x(mfn) - offset;
+ new_entry.mfn = mfn_x(mfn) - offset;
- if ( (ept_entry->avail1 == p2m_ram_logdirty)
+ if ( (new_entry.avail1 == p2m_ram_logdirty)
&& (p2mt == p2m_ram_rw) )
for ( i = 0; i < 512; i++ )
paging_mark_dirty(d, mfn_x(mfn) - offset + i);
}
else
{
- if ( ept_entry->mfn == mfn_x(mfn) )
+ if ( new_entry.mfn == mfn_x(mfn) )
need_modify_vtd_table = 0;
else
- ept_entry->mfn = mfn_x(mfn);
+ new_entry.mfn = mfn_x(mfn);
}
- ept_entry->avail1 = p2mt;
- ept_entry->avail2 = 0;
+ new_entry.avail1 = p2mt;
+ new_entry.avail2 = 0;
+
+ ept_p2m_type_to_flags(&new_entry, p2mt);
- ept_p2m_type_to_flags(ept_entry, p2mt);
+ ept_entry->epte = new_entry.epte;
}
else
ept_entry->epte = 0;