|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v15 10/10] x86/shadow: limit the number of pages which may be in use as shadows
In order to bound the amount of work a single invocation of
shadow_unhook_mappings() may be doing all in one go, constrain the
number of pages which may be in use as shadows. To achieve that, simply
adjust the "success" exit condition of _shadow_prealloc(), thus forcing
removal of shadows not only when we're short of memory.
Note that, depending on workload, this may have a severe effect on
performance, due to the potentially much larger rate of thrashed
shadows.
In the context of "x86/shadow: account for log-dirty mode when pre-
allocating" it is relevant to note that we will be too strict in
sh_prealloc_okay() when log-dirty mode is enabled: Only part of the
pages considered are actually to become shadows. But I think accepting
this is better than further complicating the logic.
Requested-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
Acked-by: Tim Deegan <tim@xxxxxxx>
---
What exactly we want the upper bound to be is up for discussion. This
may need to go together with an upper limit on the number of vCPU-s we
deem supportable in a (shadow) guest.
Really 32-bit HVM guests have 3 monitor tables. But I think that not
accounting for that in sh_prealloc_okay() is acceptable. How to
correctly do such accounting there would be unclear anyway, as we mean
to only take domain properties into account, whereas mode dependent
properties are per-vCPU.
Backporting note: The placement of the setting of the new per-domain
field relies on d->max_vcpus being set right at domain
creation. Hence this will need to move elsewhere for
4.11 and older (perhaps into shadow_set_allocation()'s
"if ( pages > 0 )" block, conditional upon the value
still being zero and max_vcpus already set).
Backporting note: 1d3668664df7 ("x86/shadow: restrict OOS allocation to
when it's really needed") is a necessary prereq for
the respective part of sh_prealloc_okay().
---
v14: Re-base over XSA-427 and new earlier patches. Restrict allowance
for monitor tables to HVM. Restrict allowance for OOS to when
that's actually in use.
v13: Prevent underflow in sh_prealloc_okay(). Re-base.
v12: Re-base past the XSA-410 series.
v11: Account for monitor tables and OOS snapshots in sh_prealloc_okay().
Calculate the (default) maximum value once during domain
initialization, into a new per-domain field.
v10: Extend commit message.
v9: New.
--- a/xen/arch/x86/include/asm/domain.h
+++ b/xen/arch/x86/include/asm/domain.h
@@ -108,6 +108,9 @@ void init_hypercall_page(struct domain *
struct shadow_domain {
#ifdef CONFIG_SHADOW_PAGING
unsigned int opt_flags; /* runtime tunable optimizations on/off */
+
+ unsigned int max_pages; /* limit on the number of shadows in use */
+
struct page_list_head pinned_shadows;
/* 1-to-1 map for use when HVM vcpus have paging disabled */
--- a/xen/arch/x86/mm/shadow/common.c
+++ b/xen/arch/x86/mm/shadow/common.c
@@ -96,6 +96,32 @@ int shadow_domain_init(struct domain *d)
d->arch.paging.flush_tlb = shadow_flush_tlb;
#endif
+ /*
+ * Figure out the default for the highest acceptable quantity of shadow
+ * memory. This is because we need to bound the amount of work potentially
+ * in need of doing by a single shadow_unhook_mappings() invocation.
+ */
+ d->arch.paging.shadow.max_pages = 2048;
+ if ( d->max_vcpus > 8 )
+ {
+ /*
+ * This is
+ *
+ * 128 * (max_vcpus + 8)
+ * max_vcpus * ---------------------
+ * max_vcpus
+ *
+ * suitably resolved, with the right side of the multiplication
+ * (when expressed as f(x)) satisfying
+ * f(8) = 256
+ * lim f(x) = 128
+ * x->∞
+ * i.e. continuous with the simpler case above and converging to
+ * shadow_min_acceptable_pages() for large values.
+ */
+ d->arch.paging.shadow.max_pages = 128 * (d->max_vcpus + 8);
+ }
+
return 0;
}
@@ -372,7 +398,7 @@ static inline void trace_shadow_prealloc
}
static bool sh_blow_tables(struct domain *d, unsigned int goal,
- bool *preempted);
+ unsigned int type, bool *preempted);
/* Make sure there are at least count pages of the order according to
* type available in the shadow page pool.
@@ -396,7 +422,7 @@ bool shadow_prealloc(struct domain *d, u
((SHF_L1_ANY | SHF_FL1_ANY) & (1u << type)) )
count += paging_logdirty_levels();
- ret = sh_blow_tables(d, count, NULL);
+ ret = sh_blow_tables(d, count, type, NULL);
if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) )
/*
* Failing to allocate memory required for shadow usage can only
result in
@@ -408,6 +434,31 @@ bool shadow_prealloc(struct domain *d, u
}
/*
+ * Check that
+ * - there are enough free pages,
+ * - there aren't too many pages in use as shadows already when about to make
+ * a (set of) new shadow page(s).
+ */
+static bool sh_prealloc_okay(const struct domain *d, unsigned int goal,
+ unsigned int type)
+{
+ if ( d->arch.paging.free_pages < goal )
+ return false;
+
+ if ( type < SH_type_min_shadow || type > SH_type_max_shadow )
+ return true;
+
+ return d->arch.paging.total_pages -
+ (d->arch.paging.free_pages - goal) <=
+ d->arch.paging.shadow.max_pages +
+#if (SHADOW_OPTIMIZATIONS & SHOPT_OUT_OF_SYNC)
+ (d->options & XEN_DOMCTL_CDF_oos_off ? 0 : SHADOW_OOS_PAGES) +
+#endif
+ /* Allow for one monitor table per HVM vCPU. */
+ paging_mode_external(d) * d->max_vcpus;
+}
+
+/*
* When @goal is zero: Deliberately free all the memory we can: This will
* tear down all of this domain's shadows.
*
@@ -415,7 +466,7 @@ bool shadow_prealloc(struct domain *d, u
* available in the shadow page pool.
*/
static bool sh_blow_tables(struct domain *d, unsigned int goal,
- bool *preempted)
+ unsigned int type, bool *preempted)
{
struct page_info *sp, *t;
struct vcpu *v;
@@ -423,7 +474,7 @@ static bool sh_blow_tables(struct domain
int i;
unsigned int done = 0;
- if ( goal && d->arch.paging.free_pages >= goal )
+ if ( goal && sh_prealloc_okay(d, goal, type) )
return true;
/*
@@ -465,7 +516,7 @@ static bool sh_blow_tables(struct domain
while ( hash_foreach(d, masks[i], callbacks, _mfn(0)) )
{
/* See if that freed up enough space */
- if ( goal && d->arch.paging.free_pages >= goal )
+ if ( goal && sh_prealloc_okay(d, goal, type) )
return true;
if ( general_preempt_check() )
@@ -491,7 +542,7 @@ static bool sh_blow_tables(struct domain
sh_unpin(d, smfn);
/* See if that freed up enough space */
- if ( goal && d->arch.paging.free_pages >= goal )
+ if ( goal && sh_prealloc_okay(d, goal, type) )
return true;
if ( preempted && !(++done & 0xff) && general_preempt_check() )
@@ -519,7 +570,7 @@ static bool sh_blow_tables(struct domain
0);
/* See if that freed up enough space */
- if ( goal && d->arch.paging.free_pages >= goal )
+ if ( goal && sh_prealloc_okay(d, goal, type) )
{
guest_flush_tlb_mask(d, d->dirty_cpumask);
return true;
@@ -569,7 +620,7 @@ static bool sh_blow_tables(struct domain
* this domain's shadows */
void shadow_blow_tables(struct domain *d, bool *preempted)
{
- sh_blow_tables(d, 0, preempted);
+ sh_blow_tables(d, 0, SH_type_none, preempted);
}
void shadow_blow_tables_per_domain(struct domain *d)
@@ -904,7 +955,7 @@ int shadow_set_allocation(struct domain
else if ( d->arch.paging.total_pages > pages )
{
/* Need to return memory to domheap */
- if ( !sh_blow_tables(d, 1, preempted) )
+ if ( !sh_blow_tables(d, 1, SH_type_none, preempted) )
return preempted && *preempted ? 0 : -ENOMEM;
sp = page_list_remove_head(&d->arch.paging.freelist);
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |