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@xxxxxxxxxx> > --- > 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) > { >