|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 3/3] x86/xstate: re-size save area when CPUID policy changes
vCPU-s get maximum size areas allocated initially. Hidden (and in
particular default-off) features may allow for a smaller size area to
suffice.
Suggested-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
Seeing that both vcpu_init_fpu() and cpuid_policy_updated() get called
from arch_vcpu_create(), I'm not sure we really need this two-stage
approach - the slightly longer period of time during which
v->arch.xsave_area would remain NULL doesn't look all that problematic.
But since xstate_alloc_save_area() gets called for idle vCPU-s, it has
to stay anyway in some form, so the extra code churn may not be worth
it.
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -294,7 +294,21 @@ void update_guest_memory_policy(struct v
}
}
-void domain_cpu_policy_changed(struct domain *d)
+/*
+ * Called during vcpu construction, and each time the toolstack changes the
+ * CPUID configuration for the domain.
+ */
+static int __must_check cpuid_policy_updated(struct vcpu *v)
+{
+ int rc = xstate_update_save_area(v);
+
+ if ( !rc && is_hvm_vcpu(v) )
+ hvm_cpuid_policy_changed(v);
+
+ return rc;
+}
+
+int domain_cpu_policy_changed(struct domain *d)
{
const struct cpuid_policy *p = d->arch.cpuid;
struct vcpu *v;
@@ -452,13 +466,18 @@ void domain_cpu_policy_changed(struct do
for_each_vcpu ( d, v )
{
- cpuid_policy_updated(v);
+ int rc = cpuid_policy_updated(v);
+
+ if ( rc )
+ return rc;
/* If PMU version is zero then the guest doesn't have VPMU */
if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
p->basic.pmu_version == 0 )
vpmu_destroy(v);
}
+
+ return 0;
}
#ifndef CONFIG_BIGMEM
@@ -597,7 +616,7 @@ int arch_vcpu_create(struct vcpu *v)
{
vpmu_initialise(v);
- cpuid_policy_updated(v);
+ rc = cpuid_policy_updated(v);
}
return rc;
@@ -841,9 +860,9 @@ int arch_domain_create(struct domain *d,
*/
d->arch.x87_fip_width = cpu_has_fpu_sel ? 0 : 8;
- domain_cpu_policy_changed(d);
-
- return 0;
+ rc = domain_cpu_policy_changed(d);
+ if ( !rc )
+ return 0;
fail:
d->is_dying = DOMDYING_dead;
@@ -2434,16 +2453,6 @@ int domain_relinquish_resources(struct d
return 0;
}
-/*
- * Called during vcpu construction, and each time the toolstack changes the
- * CPUID configuration for the domain.
- */
-void cpuid_policy_updated(struct vcpu *v)
-{
- if ( is_hvm_vcpu(v) )
- hvm_cpuid_policy_changed(v);
-}
-
void arch_dump_domain_info(struct domain *d)
{
paging_dump_domain_info(d);
--- a/xen/arch/x86/domctl.c
+++ b/xen/arch/x86/domctl.c
@@ -91,7 +91,7 @@ static int update_domain_cpu_policy(stru
recalculate_cpuid_policy(d);
/* Recalculate relevant dom/vcpu state now the policy has changed. */
- domain_cpu_policy_changed(d);
+ ret = domain_cpu_policy_changed(d);
out:
/* Free whichever cpuid/msr structs are not installed in struct domain. */
--- a/xen/arch/x86/xstate.c
+++ b/xen/arch/x86/xstate.c
@@ -541,6 +541,41 @@ int xstate_alloc_save_area(struct vcpu *
return 0;
}
+
+int xstate_update_save_area(struct vcpu *v)
+{
+ unsigned int i, size, old;
+ struct xsave_struct *save_area;
+ uint64_t xcr0_max = cpuid_policy_xcr0_max(v->domain->arch.cpuid);
+
+ ASSERT(!is_idle_vcpu(v));
+
+ if ( !cpu_has_xsave )
+ return 0;
+
+ if ( v->arch.xcr0_accum & ~xcr0_max )
+ return -EBUSY;
+
+ for ( size = old = XSTATE_AREA_MIN_SIZE, i = 2; i < xstate_features; ++i )
+ {
+ if ( xcr0_max & (1ull << i) )
+ size = max(size, xstate_offsets[i] + xstate_sizes[i]);
+ if ( v->arch.xcr0_accum & (1ull << i) )
+ old = max(old, xstate_offsets[i] + xstate_sizes[i]);
+ }
+
+ save_area = _xvrealloc(v->arch.xsave_area, size, __alignof(*save_area));
+ if ( !save_area )
+ return -ENOMEM;
+
+ ASSERT(old <= size);
+ memset((void *)save_area + old, 0, size - old);
+
+ v->arch.xsave_area = save_area;
+ v->arch.fpu_ctxt = &v->arch.xsave_area->fpu_sse;
+
+ return 0;
+}
void xstate_free_save_area(struct vcpu *v)
{
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -78,8 +78,6 @@ void toggle_guest_mode(struct vcpu *);
/* x86/64: toggle guest page tables between kernel and user modes. */
void toggle_guest_pt(struct vcpu *);
-void cpuid_policy_updated(struct vcpu *v);
-
/*
* Initialise a hypercall-transfer page. The given pointer must be mapped
* in Xen virtual address space (accesses are not validated or checked).
@@ -667,7 +665,7 @@ struct guest_memory_policy
void update_guest_memory_policy(struct vcpu *v,
struct guest_memory_policy *policy);
-void domain_cpu_policy_changed(struct domain *d);
+int __must_check domain_cpu_policy_changed(struct domain *d);
bool update_runstate_area(struct vcpu *);
bool update_secondary_system_time(struct vcpu *,
--- a/xen/include/asm-x86/xstate.h
+++ b/xen/include/asm-x86/xstate.h
@@ -106,6 +106,7 @@ void compress_xsave_states(struct vcpu *
/* extended state init and cleanup functions */
void xstate_free_save_area(struct vcpu *v);
int xstate_alloc_save_area(struct vcpu *v);
+int xstate_update_save_area(struct vcpu *v);
void xstate_init(struct cpuinfo_x86 *c);
unsigned int xstate_ctxt_size(u64 xcr0);
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |