|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v2 1/2] VMX: fix VMCS race on context-switch paths
>>> On 02.11.17 at 20:46, <igor.druzhinin@xxxxxxxxxx> wrote:
>> Any ideas about the root cause of the fault and suggestions how to reproduce
>> it
>> would be welcome. Does this crash really has something to do with PML? I
>> doubt
>> because the original environment may hardly be called PML-heavy.
Well, PML-heaviness doesn't matter. It's the mere fact that PML
is enabled on the vCPU being destroyed.
> So we finally have complete understanding of what's going on:
>
> Some vCPU has just migrated to another pCPU and we switched to idle but
> per_cpu(curr_vcpu) on the current pCPU is still pointing to it - this is
> how the current logic works. While we're in idle we're issuing
> vcpu_destroy() for some other domain which eventually calls
> vmx_vcpu_disable_pml() and trashes VMCS pointer on the current pCPU. At
> this moment we get a TLB flush IPI from that same vCPU which is now
> context switching on another pCPU - it appears to clean TLB after
> itself. This vCPU is already marked is_running=1 by the scheduler. In
> the IPI handler we enter __sync_local_execstate() and trying to call
> vmx_ctxt_switch_from() for the migrated vCPU which is supposed to call
> vmcs_reload() but doesn't do it because is_running==1. The next VMWRITE
> crashes the hypervisor.
>
> So the state transition diagram might look like:
> pCPU1: vCPUx -> migrate to pCPU2 -> idle -> RCU callbacks ->
I'm not really clear about who/what is "idle" here: pCPU1,
pCPU2, or yet something else? If vCPUx migrated to pCPU2,
wouldn't it be put back into runnable state right away, and
hence pCPU2 can't be idle at this point? Yet for pCPU1 I don't
think its idleness would matter much, i.e. the situation could
also arise without it becoming idle afaics. pCPU1 making it
anywhere softirqs are being processed would suffice.
> vcpu_destroy() -> vmx_vcpu_disable_pml() -> vmcs_clear()
> pCPU2: context switch into vCPUx -> is_running = 1 -> TLB flush
> pCPU1: IPI handler -> context switch out of vCPUx -> VMWRITE -> CRASH!
>
> We can basically just fix the condition around vmcs_reload() call but
> I'm not completely sure that it's the right way to do - I don't think
> leaving per_cpu(curr_vcpu) pointing to a migrated vCPU is a good idea
> (maybe we need to clean it). What are your thoughts?
per_cpu(curr_vcpu) can only validly be written inside
__context_switch(), hence the only way to achieve this would
be to force __context_switch() to be called earlier than out of
the TLB flush IPI handler, perhaps like in the (untested!) patch
below. Two questions then remain:
- Should we perhaps rather do this in an arch-independent way
(i.e. ahead of the call to vcpu_destroy() in common code)?
- This deals with only a special case of the more general "TLB
flush behind the back of a vmx_vmcs_enter() /
vmx_vmcs_exit() section" - does this need dealing with in a
more general way? Here I'm thinking of introducing a
FLUSH_STATE flag to be passed to flush_mask() instead of
the current flush_tlb_mask() in context_switch() and
sync_vcpu_execstate(). This could at the same time be used
for a small performance optimization: At least for HAP vCPU-s
I don't think we really need the TLB part of the flushes here.
Jan
--- unstable.orig/xen/arch/x86/domain.c
+++ unstable/xen/arch/x86/domain.c
@@ -379,6 +379,14 @@ int vcpu_initialise(struct vcpu *v)
void vcpu_destroy(struct vcpu *v)
{
+ /*
+ * Flush all state for this vCPU before fully tearing it down. This is
+ * particularly important for HVM ones on VMX, so that this flushing of
+ * state won't happen from the TLB flush IPI handler behind the back of
+ * a vmx_vmcs_enter() / vmx_vmcs_exit() section.
+ */
+ sync_vcpu_execstate(v);
+
xfree(v->arch.vm_event);
v->arch.vm_event = NULL;
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |