[PATCH 1/9] KVM: arm/arm64: vgic: Defer touching GICH_VMCR to vcpu_load/put
Marc Zyngier
marc.zyngier at arm.com
Tue Mar 21 03:29:08 PDT 2017
On 20/03/17 10:58, Christoffer Dall wrote:
> From: Christoffer Dall <cdall at cs.columbia.edu>
>
> We don't have to save/restore the VMCR on every entry to/from the guest,
> since on GICv2 we can access the control interface from EL1 and on VHE
> systems with GICv3 we can access the control interface from KVM running
> in EL2.
>
> GICv3 systems without VHE becomes the rare case, which has to
> save/restore the register on each round trip.
>
> For in-context accesses to the VMCR, meaning emulation code needs access
> to the value of the VCPU that is currently running within vcpu_load
> and vcpu_put will detect that the it should load the VMCR from the
> physical GIC instead of using the cached memory value. While this extra
> checking adds some code complexity we end up moving code out of the
> critical path.
>
> Note that userspace accesses may see out-of-date values if the VCPU is
> running while accessing the VGIC state via the KVM device API, but this
> is already the case.
>
> Signed-off-by: Christoffer Dall <cdall at cs.columbia.edu>
> Signed-off-by: Christoffer Dall <cdall at linaro.org>
> ---
> arch/arm/include/asm/kvm_asm.h | 3 +++
> arch/arm/kvm/arm.c | 11 +++++-----
> arch/arm64/include/asm/kvm_asm.h | 2 ++
> include/kvm/arm_vgic.h | 3 +++
> virt/kvm/arm/hyp/vgic-v2-sr.c | 3 ---
> virt/kvm/arm/hyp/vgic-v3-sr.c | 14 ++++++++----
> virt/kvm/arm/vgic/vgic-init.c | 12 +++++++++++
> virt/kvm/arm/vgic/vgic-v2.c | 46 ++++++++++++++++++++++++++++++++++++++--
> virt/kvm/arm/vgic/vgic-v3.c | 42 ++++++++++++++++++++++++++++++++++--
> virt/kvm/arm/vgic/vgic.c | 22 +++++++++++++++++++
> virt/kvm/arm/vgic/vgic.h | 6 ++++++
> 11 files changed, 148 insertions(+), 16 deletions(-)
>
> diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h
> index 8ef0538..dd16044 100644
> --- a/arch/arm/include/asm/kvm_asm.h
> +++ b/arch/arm/include/asm/kvm_asm.h
> @@ -75,7 +75,10 @@ extern void __init_stage2_translation(void);
> extern void __kvm_hyp_reset(unsigned long);
>
> extern u64 __vgic_v3_get_ich_vtr_el2(void);
> +extern u64 __vgic_v3_read_vmcr(void);
> +extern void __vgic_v3_write_vmcr(u32 vmcr);
> extern void __vgic_v3_init_lrs(void);
> +
> #endif
>
> #endif /* __ARM_KVM_ASM_H__ */
> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
> index d775aac..cfdf2f5 100644
> --- a/arch/arm/kvm/arm.c
> +++ b/arch/arm/kvm/arm.c
> @@ -348,15 +348,14 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
> vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
>
> kvm_arm_set_running_vcpu(vcpu);
> +
> + kvm_vgic_load(vcpu);
> }
>
> void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
> {
> - /*
> - * The arch-generic KVM code expects the cpu field of a vcpu to be -1
> - * if the vcpu is no longer assigned to a cpu. This is used for the
> - * optimized make_all_cpus_request path.
> - */
> + kvm_vgic_put(vcpu);
> +
> vcpu->cpu = -1;
>
> kvm_arm_set_running_vcpu(NULL);
> @@ -630,7 +629,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
> * non-preemptible context.
> */
> preempt_disable();
> +
> kvm_pmu_flush_hwstate(vcpu);
> +
> kvm_timer_flush_hwstate(vcpu);
> kvm_vgic_flush_hwstate(vcpu);
>
> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
> index ec3553eb..49f99cd 100644
> --- a/arch/arm64/include/asm/kvm_asm.h
> +++ b/arch/arm64/include/asm/kvm_asm.h
> @@ -59,6 +59,8 @@ extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu);
> extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
>
> extern u64 __vgic_v3_get_ich_vtr_el2(void);
> +extern u64 __vgic_v3_read_vmcr(void);
> +extern void __vgic_v3_write_vmcr(u32 vmcr);
> extern void __vgic_v3_init_lrs(void);
>
> extern u32 __kvm_get_mdcr_el2(void);
> diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
> index c0b3d99..8d7c3a7 100644
> --- a/include/kvm/arm_vgic.h
> +++ b/include/kvm/arm_vgic.h
> @@ -307,6 +307,9 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq);
>
> int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
>
> +void kvm_vgic_load(struct kvm_vcpu *vcpu);
> +void kvm_vgic_put(struct kvm_vcpu *vcpu);
> +
> #define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
> #define vgic_initialized(k) ((k)->arch.vgic.initialized)
> #define vgic_ready(k) ((k)->arch.vgic.ready)
> diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
> index c8aeb7b..d3d3b9b 100644
> --- a/virt/kvm/arm/hyp/vgic-v2-sr.c
> +++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
> @@ -114,8 +114,6 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
> if (!base)
> return;
>
> - cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
> -
> if (vcpu->arch.vgic_cpu.live_lrs) {
> cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
>
> @@ -165,7 +163,6 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
> }
> }
>
> - writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
> vcpu->arch.vgic_cpu.live_lrs = live_lrs;
> }
>
> diff --git a/virt/kvm/arm/hyp/vgic-v3-sr.c b/virt/kvm/arm/hyp/vgic-v3-sr.c
> index 3947095..e51ee7e 100644
> --- a/virt/kvm/arm/hyp/vgic-v3-sr.c
> +++ b/virt/kvm/arm/hyp/vgic-v3-sr.c
> @@ -159,8 +159,6 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
> if (!cpu_if->vgic_sre)
> dsb(st);
>
> - cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
> -
> if (vcpu->arch.vgic_cpu.live_lrs) {
> int i;
> u32 max_lr_idx, nr_pri_bits;
> @@ -261,8 +259,6 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
> live_lrs |= (1 << i);
> }
>
> - write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
> -
> if (live_lrs) {
> write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
>
> @@ -326,3 +322,13 @@ u64 __hyp_text __vgic_v3_get_ich_vtr_el2(void)
> {
> return read_gicreg(ICH_VTR_EL2);
> }
> +
> +u64 __hyp_text __vgic_v3_read_vmcr(void)
> +{
> + return read_gicreg(ICH_VMCR_EL2);
> +}
> +
> +void __hyp_text __vgic_v3_write_vmcr(u32 vmcr)
> +{
> + write_gicreg(vmcr, ICH_VMCR_EL2);
> +}
> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
> index 702f810..3762fd1 100644
> --- a/virt/kvm/arm/vgic/vgic-init.c
> +++ b/virt/kvm/arm/vgic/vgic-init.c
> @@ -262,6 +262,18 @@ int vgic_init(struct kvm *kvm)
> vgic_debug_init(kvm);
>
> dist->initialized = true;
> +
> + /*
> + * If we're initializing GICv2 on-demand when first running the VCPU
> + * then we need to load the VGIC state onto the CPU. We can detect
> + * this easily by checking if we are in between vcpu_load and vcpu_put
> + * when we just initialized the VGIC.
> + */
> + preempt_disable();
> + vcpu = kvm_arm_get_running_vcpu();
> + if (vcpu)
> + kvm_vgic_load(vcpu);
> + preempt_enable();
> out:
> return ret;
> }
> diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
> index 94cf4b9..dfe6e5e 100644
> --- a/virt/kvm/arm/vgic/vgic-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-v2.c
> @@ -199,6 +199,9 @@ void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr)
>
> void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
> {
> + struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
> + struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> + int cpu;
> u32 vmcr;
>
> vmcr = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK;
> @@ -209,12 +212,35 @@ void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
> vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) &
> GICH_VMCR_PRIMASK_MASK;
>
> - vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr;
> + cpu_if->vgic_vmcr = vmcr;
> +
> + cpu = get_cpu();
> + if (vcpu->cpu == cpu)
> + /*
> + * We have run vcpu_load and we're on VHE which means the VMCR
> + * has already been flushed to the CPU; flush it again.
> + */
I don't understand this comment. What has VHE to do with this?
> + writel_relaxed(vmcr, vgic->vctrl_base + GICH_VMCR);
> + put_cpu();
There is one thing I don't quite get: I thought we could only get there
with a userspace access. I understand that we've run vcpu_load()
already, and that we end-up with a stale configuration in the HW. But
why do we need to immediately propagate it? Surely we can rely on the
vcpu thread doing a vcpu_load itself to load the new value, right? I
must be missing something...
> }
>
> void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
> {
> - u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr;
> + struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
> + struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> + int cpu;
> + u32 vmcr;
> +
> + cpu = get_cpu();
> + if (vcpu->cpu == cpu)
> + /*
> + * We have run vcpu_load and which means the VMCR
> + * has already been flushed to the CPU; sync it back.
> + */
> + cpu_if->vgic_vmcr = readl_relaxed(vgic->vctrl_base + GICH_VMCR);
> + put_cpu();
> +
> + vmcr = cpu_if->vgic_vmcr;
Same comment as above.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
More information about the linux-arm-kernel
mailing list