[PATCH 32/37] KVM: arm/arm64: Handle VGICv2 save/restore from the main VGIC code
Andre Przywara
andre.przywara at arm.com
Wed Nov 15 09:50:07 PST 2017
Hi,
those last few patches are actually helpful for the Xen port ...
On 12/10/17 11:41, Christoffer Dall wrote:
> We can program the GICv2 hypervisor control interface logic directly
> from the core vgic code and can instead do the save/restore directly
> from the flush/sync functions, which can lead to a number of future
> optimizations.
>
> Signed-off-by: Christoffer Dall <christoffer.dall at linaro.org>
> ---
> arch/arm/kvm/hyp/switch.c | 4 --
> arch/arm64/include/asm/kvm_hyp.h | 2 -
> arch/arm64/kvm/hyp/switch.c | 4 --
> virt/kvm/arm/hyp/vgic-v2-sr.c | 83 ------------------------------------
> virt/kvm/arm/vgic/vgic-init.c | 22 ++++++----
> virt/kvm/arm/vgic/vgic-v2.c | 92 ++++++++++++++++++++++++++++++++++++++++
> virt/kvm/arm/vgic/vgic.c | 21 ++++++++-
> virt/kvm/arm/vgic/vgic.h | 5 +++
> 8 files changed, 130 insertions(+), 103 deletions(-)
>
> diff --git a/arch/arm/kvm/hyp/switch.c b/arch/arm/kvm/hyp/switch.c
> index c3b9799..0d834f8 100644
> --- a/arch/arm/kvm/hyp/switch.c
> +++ b/arch/arm/kvm/hyp/switch.c
> @@ -91,16 +91,12 @@ static void __hyp_text __vgic_save_state(struct kvm_vcpu *vcpu)
> {
> if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> __vgic_v3_save_state(vcpu);
> - else
> - __vgic_v2_save_state(vcpu);
> }
>
> static void __hyp_text __vgic_restore_state(struct kvm_vcpu *vcpu)
> {
> if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> __vgic_v3_restore_state(vcpu);
> - else
> - __vgic_v2_restore_state(vcpu);
> }
>
> static bool __hyp_text __populate_fault_info(struct kvm_vcpu *vcpu)
> diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
> index 28d5f3c..bd3fe64 100644
> --- a/arch/arm64/include/asm/kvm_hyp.h
> +++ b/arch/arm64/include/asm/kvm_hyp.h
> @@ -121,8 +121,6 @@ typeof(orig) * __hyp_text fname(void) \
> return val; \
> }
>
> -void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
> -void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
> int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu);
>
> void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
> index 5692aa0..90da506 100644
> --- a/arch/arm64/kvm/hyp/switch.c
> +++ b/arch/arm64/kvm/hyp/switch.c
> @@ -186,16 +186,12 @@ static void __hyp_text __vgic_save_state(struct kvm_vcpu *vcpu)
> {
> if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> __vgic_v3_save_state(vcpu);
> - else
> - __vgic_v2_save_state(vcpu);
> }
>
> static void __hyp_text __vgic_restore_state(struct kvm_vcpu *vcpu)
> {
> if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> __vgic_v3_restore_state(vcpu);
> - else
> - __vgic_v2_restore_state(vcpu);
> }
>
> static bool __hyp_text __true_value(void)
> diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
> index a3f18d3..b433257 100644
> --- a/virt/kvm/arm/hyp/vgic-v2-sr.c
> +++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
> @@ -22,89 +22,6 @@
> #include <asm/kvm_emulate.h>
> #include <asm/kvm_hyp.h>
>
> -static void __hyp_text save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
> -{
> - struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> - int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
> - u32 elrsr0, elrsr1;
> -
> - elrsr0 = readl_relaxed(base + GICH_ELRSR0);
> - if (unlikely(nr_lr > 32))
> - elrsr1 = readl_relaxed(base + GICH_ELRSR1);
> - else
> - elrsr1 = 0;
> -
> -#ifdef CONFIG_CPU_BIG_ENDIAN
> - cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
> -#else
> - cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
> -#endif
> -}
> -
> -static void __hyp_text save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
> -{
> - struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> - int i;
> - u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
> -
> - for (i = 0; i < used_lrs; i++) {
> - if (cpu_if->vgic_elrsr & (1UL << i))
> - cpu_if->vgic_lr[i] &= ~GICH_LR_STATE;
> - else
> - cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
> -
> - writel_relaxed(0, base + GICH_LR0 + (i * 4));
> - }
> -}
> -
> -/* vcpu is already in the HYP VA space */
> -void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
> -{
> - struct kvm *kvm = kern_hyp_va(vcpu->kvm);
> - struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> - struct vgic_dist *vgic = &kvm->arch.vgic;
> - void __iomem *base = kern_hyp_va(vgic->vctrl_base);
> - u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
> -
> - if (!base)
> - return;
> -
> - if (used_lrs) {
> - cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
> -
> - save_elrsr(vcpu, base);
> - save_lrs(vcpu, base);
> -
> - writel_relaxed(0, base + GICH_HCR);
> - } else {
> - cpu_if->vgic_elrsr = ~0UL;
> - cpu_if->vgic_apr = 0;
> - }
> -}
> -
> -/* vcpu is already in the HYP VA space */
> -void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
> -{
> - struct kvm *kvm = kern_hyp_va(vcpu->kvm);
> - struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> - struct vgic_dist *vgic = &kvm->arch.vgic;
> - void __iomem *base = kern_hyp_va(vgic->vctrl_base);
> - int i;
> - u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
> -
> - if (!base)
> - return;
> -
> - if (used_lrs) {
> - writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
> - writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
> - for (i = 0; i < used_lrs; i++) {
> - writel_relaxed(cpu_if->vgic_lr[i],
> - base + GICH_LR0 + (i * 4));
> - }
> - }
> -}
> -
> #ifdef CONFIG_ARM64
> /*
> * __vgic_v2_perform_cpuif_access -- perform a GICV access on behalf of the
> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
> index 5801261f..fa2b565 100644
> --- a/virt/kvm/arm/vgic/vgic-init.c
> +++ b/virt/kvm/arm/vgic/vgic-init.c
> @@ -425,14 +425,16 @@ static int vgic_init_cpu_dying(unsigned int cpu)
> return 0;
> }
>
> -static irqreturn_t vgic_maintenance_handler(int irq, void *data)
> +static irqreturn_t vgic_v3_maintenance_handler(int irq, void *data)
> {
> - /*
> - * We cannot rely on the vgic maintenance interrupt to be
> - * delivered synchronously. This means we can only use it to
> - * exit the VM, and we perform the handling of EOIed
> - * interrupts on the exit path (see vgic_process_maintenance).
> - */
I always found this comment quite enlightening, especially as it points
out that we need to deviate somewhat from the architectural idea here.
I see that you have shortened it below. Is it no longer true? Can we
keep the more elaborate version? If not here, then below?
> + BUG(); /* Not implemented lazy save/restore on GICv3 */
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t vgic_v2_maintenance_handler(int irq, void *data)
> +{
> + struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data;
I think you need an empty line here, to separate variable declaration
from code.
> + vgic_v2_handle_maintenance(vcpu);
> return IRQ_HANDLED;
> }
>
> @@ -464,6 +466,7 @@ void kvm_vgic_init_cpu_hardware(void)
> int kvm_vgic_hyp_init(void)
> {
> const struct gic_kvm_info *gic_kvm_info;
> + irqreturn_t (*handler)(int irq, void *data);
> int ret;
>
> gic_kvm_info = gic_get_kvm_info();
> @@ -478,6 +481,7 @@ int kvm_vgic_hyp_init(void)
> switch (gic_kvm_info->type) {
> case GIC_V2:
> ret = vgic_v2_probe(gic_kvm_info);
> + handler = vgic_v2_maintenance_handler;
> break;
1> case GIC_V3:
> ret = vgic_v3_probe(gic_kvm_info);
> @@ -485,6 +489,7 @@ int kvm_vgic_hyp_init(void)
> static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
> kvm_info("GIC system register CPU interface enabled\n");
> }
> + handler = vgic_v3_maintenance_handler;
> break;
> default:
> ret = -ENODEV;
> @@ -494,8 +499,7 @@ int kvm_vgic_hyp_init(void)
> return ret;
>
> kvm_vgic_global_state.maint_irq = gic_kvm_info->maint_irq;
> - ret = request_percpu_irq(kvm_vgic_global_state.maint_irq,
> - vgic_maintenance_handler,
> + ret = request_percpu_irq(kvm_vgic_global_state.maint_irq, handler,
> "vgic", kvm_get_running_vcpus());
> if (ret) {
> kvm_err("Cannot register interrupt %d\n",
> diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
> index 8089710..259079b 100644
> --- a/virt/kvm/arm/vgic/vgic-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-v2.c
> @@ -37,6 +37,17 @@ void vgic_v2_init_lrs(void)
> vgic_v2_write_lr(i, 0);
> }
>
> +void vgic_v2_handle_maintenance(struct kvm_vcpu *vcpu)
> +{
> + void __iomem *base = kvm_vgic_global_state.vctrl_base;
> +
> + /*
> + * Disable maintenance interrupt as we only use it to generate an exit
> + * from the VM.
> + */
Isn't that comment a bit misleading, as writing 0 to HCR not only
disables all interrupt sources, but also the whole GICV interface
altogether (bit 0: EN)?
I see that it gets enabled later on when writing ->vgic_hcr into the
register, but this function here looks a bit surprising to me.
In general these changes to the interrupt handling leave me a bit
puzzled. Should this be a separate patch? Or explained in the commit
message?
> + writel_relaxed(0, base + GICH_HCR);
> +}
> +
> void vgic_v2_set_underflow(struct kvm_vcpu *vcpu)
> {
> struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
> @@ -393,6 +404,87 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
> return ret;
> }
>
> +static void save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
> +{
> + struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> + int nr_lr = kvm_vgic_global_state.nr_lr;
> + u32 elrsr0, elrsr1;
> +
> + elrsr0 = readl_relaxed(base + GICH_ELRSR0);
> + if (unlikely(nr_lr > 32))
> + elrsr1 = readl_relaxed(base + GICH_ELRSR1);
> + else
> + elrsr1 = 0;
> +
> +#ifdef CONFIG_CPU_BIG_ENDIAN
> + cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
> +#else
> + cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
> +#endif
I have some gut feeling that this is really broken, since we mix up
endian *byte* ordering with *bit* ordering here, don't we?
I understand it's just copied and gets removed later on, so I was
wondering if you could actually move patch 35/37 ("Get rid of
vgic_elrsr") before this patch here, to avoid copying bogus code around?
Or does 35/37 depend on 34/37 to be correct?
> +}
> +
> +static void save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
> +{
> + struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> + int i;
> + u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
> +
> + for (i = 0; i < used_lrs; i++) {
> + if (cpu_if->vgic_elrsr & (1UL << i))
> + cpu_if->vgic_lr[i] &= ~GICH_LR_STATE;
> + else
> + cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
> +
> + writel_relaxed(0, base + GICH_LR0 + (i * 4));
> + }
> +}
> +
> +void vgic_v2_save_state(struct kvm_vcpu *vcpu)
> +{
> + struct kvm *kvm = vcpu->kvm;
> + struct vgic_dist *vgic = &kvm->arch.vgic;
> + struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> + void __iomem *base = vgic->vctrl_base;
> + u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
> +
> + if (!base)
> + return;
> +
> + if (used_lrs) {
> + cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
> +
> + save_elrsr(vcpu, base);
> + save_lrs(vcpu, base);
> +
> + writel_relaxed(0, base + GICH_HCR);
> + } else {
> + cpu_if->vgic_elrsr = ~0UL;
> + cpu_if->vgic_apr = 0;
> + }
> +}
> +
> +void vgic_v2_restore_state(struct kvm_vcpu *vcpu)
> +{
> + struct kvm *kvm = vcpu->kvm;
> + struct vgic_dist *vgic = &kvm->arch.vgic;
> + struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> + void __iomem *base = vgic->vctrl_base;
> + u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
> + int i;
> +
> + if (!base)
> + return;
> +
> + if (used_lrs) {
> + writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
> + writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
> + for (i = 0; i < used_lrs; i++) {
> + writel_relaxed(cpu_if->vgic_lr[i],
> + base + GICH_LR0 + (i * 4));
> + }
> + }
> +}
> +
> void vgic_v2_load(struct kvm_vcpu *vcpu)
> {
> struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
> diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
> index b1bd238..790fd66 100644
> --- a/virt/kvm/arm/vgic/vgic.c
> +++ b/virt/kvm/arm/vgic/vgic.c
> @@ -18,6 +18,8 @@
> #include <linux/kvm_host.h>
> #include <linux/list_sort.h>
>
> +#include <asm/kvm_hyp.h>
Why do you need that? Commenting this out seems to compile anyway for me.
> +
> #include "vgic.h"
>
> #define CREATE_TRACE_POINTS
> @@ -683,11 +685,19 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
> vgic_clear_lr(vcpu, count);
> }
>
> +static inline void vgic_save_state(struct kvm_vcpu *vcpu)
Isn't "inline" frowned upon in .c files?
> +{
> + if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> + vgic_v2_save_state(vcpu);
> +}
> +
> /* Sync back the hardware VGIC state into our emulation after a guest's run. */
> void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
> {
> struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
>
> + vgic_save_state(vcpu);
> +
> /* An empty ap_list_head implies used_lrs == 0 */
> if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head))
> return;
> @@ -697,6 +707,12 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
> vgic_prune_ap_list(vcpu);
> }
>
> +static inline void vgic_restore_state(struct kvm_vcpu *vcpu)
Same here.
Apart from those rather cosmetic issues I have at least verified that
the code is actually moved from vgic-v2-sr.c to vgic-v2.c, plus/minus
the required changes when moving this from HYP to vGIC/EL1.
Cheers,
Andre.
> +{
> + if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> + vgic_v2_restore_state(vcpu);
> +}
> +
> /* Flush our emulation state into the GIC hardware before entering the guest. */
> void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
> {
> @@ -710,13 +726,16 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
> * this.
> */
> if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head))
> - return;
> + goto out;
>
> DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
>
> spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
> vgic_flush_lr_state(vcpu);
> spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
> +
> +out:
> + vgic_restore_state(vcpu);
> }
>
> void kvm_vgic_load(struct kvm_vcpu *vcpu)
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index 4f8aecb..9895396 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -167,6 +167,11 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
> void vgic_v2_init_lrs(void);
> void vgic_v2_load(struct kvm_vcpu *vcpu);
> void vgic_v2_put(struct kvm_vcpu *vcpu);
> +void vgic_v2_handle_maintenance(struct kvm_vcpu *vcpu);
> +
> +void vgic_v2_save_state(struct kvm_vcpu *vcpu);
> +void vgic_v2_restore_state(struct kvm_vcpu *vcpu);
> +
>
> static inline void vgic_get_irq_kref(struct vgic_irq *irq)
> {
>
More information about the linux-arm-kernel
mailing list