[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [v4 16/17] vmx: Add some scheduler hooks for VT-d posted interrupts
This patch adds the following arch hooks in scheduler: - vmx_pre_ctx_switch_pi(): It is called before context switch, we update the posted interrupt descriptor when the vCPU is preempted, go to sleep, or is blocked. - vmx_post_ctx_switch_pi() It is called after context switch, we update the posted interrupt descriptor when the vCPU is going to run. - arch_vcpu_wake() It will be called when waking up the vCPU, we update the posted interrupt descriptor when the vCPU is unblocked. CC: Keir Fraser <keir@xxxxxxx> CC: Jan Beulich <jbeulich@xxxxxxxx> CC: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> CC: Kevin Tian <kevin.tian@xxxxxxxxx> CC: George Dunlap <george.dunlap@xxxxxxxxxxxxx> CC: Dario Faggioli <dario.faggioli@xxxxxxxxxx> Sugguested-by: Dario Faggioli <dario.faggioli@xxxxxxxxxx> Signed-off-by: Feng Wu <feng.wu@xxxxxxxxx> --- v4: - Newly added xen/arch/x86/domain.c | 10 +++ xen/arch/x86/hvm/vmx/vmx.c | 145 +++++++++++++++++++++++++++++++++++++ xen/common/schedule.c | 2 + xen/include/asm-x86/domain.h | 3 + xen/include/asm-x86/hvm/hvm.h | 2 + xen/include/asm-x86/hvm/vmx/vmcs.h | 8 ++ 6 files changed, 170 insertions(+) diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index db073a6..36fd2d5 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -1550,9 +1550,19 @@ void context_switch(struct vcpu *prev, struct vcpu *next) set_current(next); + /* + * We need to update posted interrupt descriptor for each context switch, + * hence cannot use the lazy context switch for this. + */ + if ( !is_idle_vcpu(prev) && prev->arch.pi_ctxt_switch_from ) + prev->arch.pi_ctxt_switch_from(prev); + if ( (per_cpu(curr_vcpu, cpu) == next) || (is_idle_vcpu(next) && cpu_online(cpu)) ) { + if ( !is_idle_vcpu(next) && next->arch.pi_ctxt_switch_to ) + next->arch.pi_ctxt_switch_to(next); + local_irq_enable(); } else diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index f2b7fe0..f8b3601 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -67,6 +67,8 @@ enum handler_return { HNDL_done, HNDL_unhandled, HNDL_exception_raised }; static void vmx_ctxt_switch_from(struct vcpu *v); static void vmx_ctxt_switch_to(struct vcpu *v); +static void vmx_pre_ctx_switch_pi(struct vcpu *v); +static void vmx_post_ctx_switch_pi(struct vcpu *v); static int vmx_alloc_vlapic_mapping(struct domain *d); static void vmx_free_vlapic_mapping(struct domain *d); @@ -116,10 +118,17 @@ static int vmx_vcpu_initialise(struct vcpu *v) INIT_LIST_HEAD(&v->arch.hvm_vmx.pi_blocked_vcpu_list); INIT_LIST_HEAD(&v->arch.hvm_vmx.pi_vcpu_on_set_list); + v->arch.hvm_vmx.pi_block_cpu = -1; + + spin_lock_init(&v->arch.hvm_vmx.pi_lock); + v->arch.schedule_tail = vmx_do_resume; v->arch.ctxt_switch_from = vmx_ctxt_switch_from; v->arch.ctxt_switch_to = vmx_ctxt_switch_to; + v->arch.pi_ctxt_switch_from = vmx_pre_ctx_switch_pi; + v->arch.pi_ctxt_switch_to = vmx_post_ctx_switch_pi; + if ( (rc = vmx_create_vmcs(v)) != 0 ) { dprintk(XENLOG_WARNING, @@ -715,6 +724,141 @@ static void vmx_fpu_leave(struct vcpu *v) } } +void arch_vcpu_wake(struct vcpu *v) +{ + unsigned long gflags; + + if ( !iommu_intpost || !is_hvm_vcpu(v) || !has_arch_pdevs(v->domain) ) + return; + + spin_lock_irqsave(&v->arch.hvm_vmx.pi_lock, gflags); + + if ( likely(vcpu_runnable(v)) || + !test_bit(_VPF_blocked, &v->pause_flags) ) + { + struct pi_desc *pi_desc = &v->arch.hvm_vmx.pi_desc; + unsigned long flags; + + /* + * We don't need to send notification event to a non-running + * vcpu, the interrupt information will be delivered to it before + * VM-ENTRY when the vcpu is scheduled to run next time. + */ + pi_set_sn(pi_desc); + + /* + * Set 'NV' feild back to posted_intr_vector, so the + * Posted-Interrupts can be delivered to the vCPU by + * VT-d HW after it is scheduled to run. + */ + write_atomic((uint8_t*)&pi_desc->nv, posted_intr_vector); + + /* + * Delete the vCPU from the related block list + * if we are resuming from blocked state + */ + if ( v->arch.hvm_vmx.pi_block_cpu != -1 ) + { + spin_lock_irqsave(&per_cpu(pi_blocked_vcpu_lock, + v->arch.hvm_vmx.pi_block_cpu), flags); + list_del_init(&v->arch.hvm_vmx.pi_blocked_vcpu_list); + spin_unlock_irqrestore(&per_cpu(pi_blocked_vcpu_lock, + v->arch.hvm_vmx.pi_block_cpu), flags); + } + } + + spin_unlock_irqrestore(&v->arch.hvm_vmx.pi_lock, gflags); +} + +static void vmx_pre_ctx_switch_pi(struct vcpu *v) +{ + struct pi_desc *pi_desc = &v->arch.hvm_vmx.pi_desc; + struct pi_desc old, new; + unsigned long flags, gflags; + + if ( !iommu_intpost || !is_hvm_vcpu(v) || !has_arch_pdevs(v->domain) ) + return; + + spin_lock_irqsave(&v->arch.hvm_vmx.pi_lock, gflags); + + if ( vcpu_runnable(v) || !test_bit(_VPF_blocked, &v->pause_flags) ) + { + /* + * The vCPU has been preempted or went to sleep. We don't need to send + * notification event to a non-running vcpu, the interrupt information + * will be delivered to it before VM-ENTRY when the vcpu is scheduled + * to run next time. + */ + pi_set_sn(pi_desc); + + } + else if ( test_bit(_VPF_blocked, &v->pause_flags) ) + { + /* + * The vCPU is blocking, we need to add it to one of the per pCPU lists. + * We save v->processor to v->arch.hvm_vmx.pi_block_cpu and use it for + * the per-CPU list, we also save it to posted-interrupt descriptor and + * make it as the destination of the wake-up notification event. + */ + v->arch.hvm_vmx.pi_block_cpu = v->processor; + spin_lock_irqsave(&per_cpu(pi_blocked_vcpu_lock, + v->arch.hvm_vmx.pi_block_cpu), flags); + list_add_tail(&v->arch.hvm_vmx.pi_blocked_vcpu_list, + &per_cpu(pi_blocked_vcpu, v->arch.hvm_vmx.pi_block_cpu)); + spin_unlock_irqrestore(&per_cpu(pi_blocked_vcpu_lock, + v->arch.hvm_vmx.pi_block_cpu), flags); + + do { + old.control = new.control = pi_desc->control; + + /* Should not block the vCPU if an interrupt was posted for it */ + + if ( pi_test_on(&old) ) + { + spin_unlock_irqrestore(&v->arch.hvm_vmx.pi_lock, gflags); + vcpu_unblock(v); + return; + } + + /* + * Change the 'NDST' field to v->arch.hvm_vmx.pi_block_cpu, + * so when external interrupts from assigned deivces happen, + * wakeup notifiction event will go to + * v->arch.hvm_vmx.pi_block_cpu, then in pi_wakeup_interrupt() + * we can find the vCPU in the right list to wake up. + */ + if ( x2apic_enabled ) + new.ndst = cpu_physical_id(v->arch.hvm_vmx.pi_block_cpu); + else + new.ndst = MASK_INSR(cpu_physical_id( + v->arch.hvm_vmx.pi_block_cpu), + PI_xAPIC_NDST_MASK); + pi_clear_sn(&new); + new.nv = pi_wakeup_vector; + } while ( cmpxchg(&pi_desc->control, old.control, new.control) + != old.control ); + } + + spin_unlock_irqrestore(&v->arch.hvm_vmx.pi_lock, gflags); +} + +static void vmx_post_ctx_switch_pi(struct vcpu *v) +{ + struct pi_desc *pi_desc = &v->arch.hvm_vmx.pi_desc; + + if ( !iommu_intpost || !is_hvm_vcpu(v) || !has_arch_pdevs(v->domain) ) + return; + + if ( x2apic_enabled ) + write_atomic(&pi_desc->ndst, cpu_physical_id(v->processor)); + else + write_atomic(&pi_desc->ndst, + MASK_INSR(cpu_physical_id(v->processor), + PI_xAPIC_NDST_MASK)); + + pi_clear_sn(pi_desc); +} + static void vmx_ctxt_switch_from(struct vcpu *v) { /* @@ -753,6 +897,7 @@ static void vmx_ctxt_switch_to(struct vcpu *v) vmx_restore_guest_msrs(v); vmx_restore_dr(v); + vmx_post_ctx_switch_pi(v); } diff --git a/xen/common/schedule.c b/xen/common/schedule.c index 6b02f98..90616c4 100644 --- a/xen/common/schedule.c +++ b/xen/common/schedule.c @@ -381,6 +381,8 @@ void vcpu_wake(struct vcpu *v) unsigned long flags; spinlock_t *lock = vcpu_schedule_lock_irqsave(v, &flags); + arch_vcpu_wake(v); + if ( likely(vcpu_runnable(v)) ) { if ( v->runstate.state >= RUNSTATE_blocked ) diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index a3c117f..ff7a995 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -446,6 +446,9 @@ struct arch_vcpu void (*ctxt_switch_from) (struct vcpu *); void (*ctxt_switch_to) (struct vcpu *); + void (*pi_ctxt_switch_from) (struct vcpu *); + void (*pi_ctxt_switch_to) (struct vcpu *); + /* Virtual Machine Extensions */ union { struct pv_vcpu pv_vcpu; diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h index 77eeac5..eeabb32 100644 --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -509,6 +509,8 @@ bool_t nhvm_vmcx_hap_enabled(struct vcpu *v); /* interrupt */ enum hvm_intblk nhvm_interrupt_blocked(struct vcpu *v); +void arch_vcpu_wake(struct vcpu *v); + #ifndef NDEBUG /* Permit use of the Forced Emulation Prefix in HVM guests */ extern bool_t opt_hvm_fep; diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h index fdaf6fb..6ed2081 100644 --- a/xen/include/asm-x86/hvm/vmx/vmcs.h +++ b/xen/include/asm-x86/hvm/vmx/vmcs.h @@ -165,6 +165,14 @@ struct arch_vmx_struct { struct list_head pi_blocked_vcpu_list; struct list_head pi_vcpu_on_set_list; + + /* + * Before vCPU is blocked, it is added to the global per-cpu list + * of 'pi_block_cpu', then VT-d engine can send wakeup notification + * event to 'pi_block_cpu' and wakeup the related vCPU. + */ + int pi_block_cpu; + spinlock_t pi_lock; }; int vmx_create_vmcs(struct vcpu *v); -- 2.1.0 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |