mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
x86/PoD: correctly handle non-order-0 decrease-reservation requests
p2m_pod_decrease_reservation() at the moment only returns a boolean value: true for "nothing more to do", false for "something more to do". If it returns false, decrease_reservation() will loop over the entire range, calling guest_remove_page() for each page. Unfortunately, in the case p2m_pod_decrease_reservation() succeeds partially, some of the memory in the range will be not-present; at which point guest_remove_page() will return an error, and the entire operation will fail. Fix this by: 1. Having p2m_pod_decrease_reservation() return exactly the number of gpfn pages it has handled (i.e., replaced with 'not present'). 2. Making guest_remove_page() return -ENOENT in the case that the gpfn in question was already empty (and in no other cases). 3. When looping over guest_remove_page(), expect the number of -ENOENT failures to be no larger than the number of pages p2m_pod_decrease_reservation() removed. Signed-off-by: Jan Beulich <jbeulich@suse.com> Signed-off-by: George Dunlap <george.dunlap@citrix.com> Acked-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Julien Grall <julien.grall@linaro.org>
This commit is contained in:
1 parent
75c47ae9b6
commit
7867181b2a
4 files changed
+52
-33
No files matched your search
+3
-3
@@ -388,10 +388,10 @@ int guest_physmap_mark_populate_on_demand(struct domain *d,
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn,
|
||||
unsigned int order)
|
||||
unsigned long p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn,
|
||||
unsigned int order)
|
||||
{
|
||||
return -ENOSYS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void p2m_set_permission(lpae_t *e, p2m_type_t t, p2m_access_t a)
|
||||
|
||||
@@ -510,11 +510,10 @@ p2m_pod_zero_check_superpage(struct p2m_domain *p2m, gfn_t gfn);
|
||||
* Once both of these functions have been completed, we can return and
|
||||
* allow decrease_reservation() to handle everything else.
|
||||
*/
|
||||
int
|
||||
unsigned long
|
||||
p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long i, n;
|
||||
unsigned long ret = 0, i, n;
|
||||
struct p2m_domain *p2m = p2m_get_hostp2m(d);
|
||||
bool_t steal_for_cache;
|
||||
long pod, nonpod, ram;
|
||||
@@ -577,9 +576,9 @@ p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
|
||||
domain_crash(d);
|
||||
goto out_unlock;
|
||||
}
|
||||
p2m->pod.entry_count -= 1UL << order;
|
||||
ret = 1UL << order;
|
||||
p2m->pod.entry_count -= ret;
|
||||
BUG_ON(p2m->pod.entry_count < 0);
|
||||
ret = 1;
|
||||
goto out_entry_check;
|
||||
}
|
||||
|
||||
@@ -630,6 +629,7 @@ p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
|
||||
p2m->pod.entry_count -= n;
|
||||
BUG_ON(p2m->pod.entry_count < 0);
|
||||
pod -= n;
|
||||
ret += n;
|
||||
}
|
||||
else if ( steal_for_cache && p2m_is_ram(t) )
|
||||
{
|
||||
@@ -664,16 +664,10 @@ p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
|
||||
|
||||
nonpod -= n;
|
||||
ram -= n;
|
||||
ret += n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no more non-PoD entries, tell decrease_reservation() that
|
||||
* there's nothing left to do.
|
||||
*/
|
||||
if ( nonpod == 0 )
|
||||
ret = 1;
|
||||
|
||||
out_entry_check:
|
||||
/* If we've reduced our "liabilities" beyond our "assets", free some */
|
||||
if ( p2m->pod.entry_count < p2m->pod.count )
|
||||
|
||||
+41
-16
@@ -288,13 +288,16 @@ int guest_remove_page(struct domain *d, unsigned long gmfn)
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
mfn = get_gfn_query(d, gmfn, &p2mt);
|
||||
if ( unlikely(p2mt == p2m_invalid) || unlikely(p2mt == p2m_mmio_dm) )
|
||||
return -ENOENT;
|
||||
|
||||
if ( unlikely(p2m_is_paging(p2mt)) )
|
||||
{
|
||||
rc = guest_physmap_remove_page(d, _gfn(gmfn), mfn, 0);
|
||||
put_gfn(d, gmfn);
|
||||
|
||||
if ( rc )
|
||||
return rc;
|
||||
goto out_put_gfn;
|
||||
|
||||
put_gfn(d, gmfn);
|
||||
|
||||
/* If the page hasn't yet been paged out, there is an
|
||||
* actual page that needs to be released. */
|
||||
@@ -312,9 +315,7 @@ int guest_remove_page(struct domain *d, unsigned long gmfn)
|
||||
if ( p2mt == p2m_mmio_direct )
|
||||
{
|
||||
rc = clear_mmio_p2m_entry(d, gmfn, mfn, PAGE_ORDER_4K);
|
||||
put_gfn(d, gmfn);
|
||||
|
||||
return rc;
|
||||
goto out_put_gfn;
|
||||
}
|
||||
#else
|
||||
mfn = gfn_to_mfn(d, _gfn(gmfn));
|
||||
@@ -339,10 +340,8 @@ int guest_remove_page(struct domain *d, unsigned long gmfn)
|
||||
rc = mem_sharing_unshare_page(d, gmfn, 0);
|
||||
if ( rc )
|
||||
{
|
||||
put_gfn(d, gmfn);
|
||||
(void)mem_sharing_notify_enomem(d, gmfn, 0);
|
||||
|
||||
return rc;
|
||||
goto out_put_gfn;
|
||||
}
|
||||
/* Maybe the mfn changed */
|
||||
mfn = get_gfn_query_unlocked(d, gmfn, &p2mt);
|
||||
@@ -379,9 +378,14 @@ int guest_remove_page(struct domain *d, unsigned long gmfn)
|
||||
put_page(page);
|
||||
|
||||
put_page(page);
|
||||
out_put_gfn: __maybe_unused
|
||||
put_gfn(d, gmfn);
|
||||
|
||||
return rc;
|
||||
/*
|
||||
* Filter out -ENOENT return values that aren't a result of an empty p2m
|
||||
* entry.
|
||||
*/
|
||||
return rc != -ENOENT ? rc : -EINVAL;
|
||||
}
|
||||
|
||||
static void decrease_reservation(struct memop_args *a)
|
||||
@@ -396,6 +400,8 @@ static void decrease_reservation(struct memop_args *a)
|
||||
|
||||
for ( i = a->nr_done; i < a->nr_extents; i++ )
|
||||
{
|
||||
unsigned long pod_done;
|
||||
|
||||
if ( i != a->nr_done && hypercall_preempt_check() )
|
||||
{
|
||||
a->preempted = 1;
|
||||
@@ -420,14 +426,33 @@ static void decrease_reservation(struct memop_args *a)
|
||||
}
|
||||
|
||||
/* See if populate-on-demand wants to handle this */
|
||||
if ( is_hvm_domain(a->domain)
|
||||
&& p2m_pod_decrease_reservation(a->domain, _gfn(gmfn),
|
||||
a->extent_order) )
|
||||
continue;
|
||||
pod_done = is_hvm_domain(a->domain) ?
|
||||
p2m_pod_decrease_reservation(a->domain, _gfn(gmfn),
|
||||
a->extent_order) : 0;
|
||||
|
||||
for ( j = 0; j < (1 << a->extent_order); j++ )
|
||||
if ( guest_remove_page(a->domain, gmfn + j) )
|
||||
/*
|
||||
* Look for pages not handled by p2m_pod_decrease_reservation().
|
||||
*
|
||||
* guest_remove_page() will return -ENOENT for pages which have already
|
||||
* been removed by p2m_pod_decrease_reservation(); so expect to see
|
||||
* exactly pod_done failures. Any more means that there were invalid
|
||||
* entries before p2m_pod_decrease_reservation() was called.
|
||||
*/
|
||||
for ( j = 0; j + pod_done < (1UL << a->extent_order); j++ )
|
||||
{
|
||||
switch ( guest_remove_page(a->domain, gmfn + j) )
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case -ENOENT:
|
||||
if ( !pod_done )
|
||||
goto out;
|
||||
--pod_done;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@@ -26,9 +26,9 @@ int unmap_mmio_regions(struct domain *d,
|
||||
|
||||
/*
|
||||
* Call when decreasing memory reservation to handle PoD entries properly.
|
||||
* Will return '1' if all entries were handled and nothing more need be done.
|
||||
* Returns the number of pages that were successfully processed.
|
||||
*/
|
||||
int
|
||||
unsigned long
|
||||
p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn,
|
||||
unsigned int order);
|
||||
|
||||
|
||||