* [PATCH v3 01/12] KVM: arm/arm64: vgic-v2: Avoid accessing GICH registers
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 02/12] KVM: arm/arm64: vgic-v2: Save maintenance interrupt state only if required Marc Zyngier
` (10 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
GICv2 registers are *slow*. As in "terrifyingly slow". Which is bad.
But we're equaly bad, as we make a point in accessing them even if
we don't have any interrupt in flight.
A good solution is to first find out if we have anything useful to
write into the GIC, and if we don't, to simply not do it. This
involves tracking which LRs actually have something valid there.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
include/kvm/arm_vgic.h | 2 ++
virt/kvm/arm/hyp/vgic-v2-sr.c | 72 ++++++++++++++++++++++++++++++-------------
2 files changed, 52 insertions(+), 22 deletions(-)
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 13a3d53..f473fd6 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -321,6 +321,8 @@ struct vgic_cpu {
/* Protected by the distributor's irq_phys_map_lock */
struct list_head irq_phys_map_list;
+
+ u64 live_lrs;
};
#define LR_EMPTY 0xff
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index 9514a7d..aa0fdb8 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -36,28 +36,41 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
nr_lr = vcpu->arch.vgic_cpu.nr_lr;
cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
- cpu_if->vgic_misr = readl_relaxed(base + GICH_MISR);
- eisr0 = readl_relaxed(base + GICH_EISR0);
- elrsr0 = readl_relaxed(base + GICH_ELRSR0);
- if (unlikely(nr_lr > 32)) {
- eisr1 = readl_relaxed(base + GICH_EISR1);
- elrsr1 = readl_relaxed(base + GICH_ELRSR1);
- } else {
- eisr1 = elrsr1 = 0;
- }
+
+ if (vcpu->arch.vgic_cpu.live_lrs) {
+ eisr0 = readl_relaxed(base + GICH_EISR0);
+ elrsr0 = readl_relaxed(base + GICH_ELRSR0);
+ cpu_if->vgic_misr = readl_relaxed(base + GICH_MISR);
+ cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
+
+ if (unlikely(nr_lr > 32)) {
+ eisr1 = readl_relaxed(base + GICH_EISR1);
+ elrsr1 = readl_relaxed(base + GICH_ELRSR1);
+ } else {
+ eisr1 = elrsr1 = 0;
+ }
+
#ifdef CONFIG_CPU_BIG_ENDIAN
- cpu_if->vgic_eisr = ((u64)eisr0 << 32) | eisr1;
- cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
+ cpu_if->vgic_eisr = ((u64)eisr0 << 32) | eisr1;
+ cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
#else
- cpu_if->vgic_eisr = ((u64)eisr1 << 32) | eisr0;
- cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
+ cpu_if->vgic_eisr = ((u64)eisr1 << 32) | eisr0;
+ cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
#endif
- cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
- writel_relaxed(0, base + GICH_HCR);
+ for (i = 0; i < nr_lr; i++)
+ if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
+ cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
- for (i = 0; i < nr_lr; i++)
- cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
+ writel_relaxed(0, base + GICH_HCR);
+
+ vcpu->arch.vgic_cpu.live_lrs = 0;
+ } else {
+ cpu_if->vgic_eisr = 0;
+ cpu_if->vgic_elrsr = ~0UL;
+ cpu_if->vgic_misr = 0;
+ cpu_if->vgic_apr = 0;
+ }
}
/* vcpu is already in the HYP VA space */
@@ -68,15 +81,30 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
struct vgic_dist *vgic = &kvm->arch.vgic;
void __iomem *base = kern_hyp_va(vgic->vctrl_base);
int i, nr_lr;
+ u64 live_lrs = 0;
if (!base)
return;
- writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
- writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
- writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
-
nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+
for (i = 0; i < nr_lr; i++)
- writel_relaxed(cpu_if->vgic_lr[i], base + GICH_LR0 + (i * 4));
+ if (cpu_if->vgic_lr[i] & GICH_LR_STATE)
+ live_lrs |= 1UL << i;
+
+ if (live_lrs) {
+ writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
+ writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
+ for (i = 0; i < nr_lr; i++) {
+ u32 val = 0;
+
+ if (live_lrs & (1UL << i))
+ val = cpu_if->vgic_lr[i];
+
+ writel_relaxed(val, base + GICH_LR0 + (i * 4));
+ }
+ }
+
+ writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
+ vcpu->arch.vgic_cpu.live_lrs = live_lrs;
}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 02/12] KVM: arm/arm64: vgic-v2: Save maintenance interrupt state only if required
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 01/12] KVM: arm/arm64: vgic-v2: Avoid accessing GICH registers Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 03/12] KVM: arm/arm64: vgic-v2: Move GICH_ELRSR saving to its own function Marc Zyngier
` (9 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
Next on our list of useless accesses is the maintenance interrupt
status registers (GICH_MISR, GICH_EISR{0,1}).
It is pointless to save them if we haven't asked for a maintenance
interrupt the first place, which can only happen for two reasons:
- Underflow: GICH_HCR_UIE will be set,
- EOI: GICH_LR_EOI will be set.
These conditions can be checked on the in-memory copies of the regs.
Should any of these two condition be valid, we must read GICH_MISR.
We can then check for GICH_MISR_EOI, and only when set read
GICH_EISR*.
This means that in most case, we don't have to save them at all.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/hyp/vgic-v2-sr.c | 54 +++++++++++++++++++++++++++++++++++++------
1 file changed, 47 insertions(+), 7 deletions(-)
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index aa0fdb8..0dd83d5 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -21,6 +21,49 @@
#include <asm/kvm_hyp.h>
+static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu,
+ void __iomem *base)
+{
+ struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
+ int nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+ u32 eisr0, eisr1;
+ int i;
+ bool expect_mi;
+
+ expect_mi = !!(cpu_if->vgic_hcr & GICH_HCR_UIE);
+
+ for (i = 0; i < nr_lr; i++) {
+ if (!(vcpu->arch.vgic_cpu.live_lrs & (1UL << i)))
+ continue;
+
+ expect_mi |= (!(cpu_if->vgic_lr[i] & GICH_LR_HW) &&
+ (cpu_if->vgic_lr[i] & GICH_LR_EOI));
+ }
+
+ if (expect_mi) {
+ cpu_if->vgic_misr = readl_relaxed(base + GICH_MISR);
+
+ if (cpu_if->vgic_misr & GICH_MISR_EOI) {
+ eisr0 = readl_relaxed(base + GICH_EISR0);
+ if (unlikely(nr_lr > 32))
+ eisr1 = readl_relaxed(base + GICH_EISR1);
+ else
+ eisr1 = 0;
+ } else {
+ eisr0 = eisr1 = 0;
+ }
+ } else {
+ cpu_if->vgic_misr = 0;
+ eisr0 = eisr1 = 0;
+ }
+
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ cpu_if->vgic_eisr = ((u64)eisr0 << 32) | eisr1;
+#else
+ cpu_if->vgic_eisr = ((u64)eisr1 << 32) | eisr0;
+#endif
+}
+
/* vcpu is already in the HYP VA space */
void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
{
@@ -28,7 +71,7 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
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);
- u32 eisr0, eisr1, elrsr0, elrsr1;
+ u32 elrsr0, elrsr1;
int i, nr_lr;
if (!base)
@@ -38,26 +81,23 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
if (vcpu->arch.vgic_cpu.live_lrs) {
- eisr0 = readl_relaxed(base + GICH_EISR0);
elrsr0 = readl_relaxed(base + GICH_ELRSR0);
- cpu_if->vgic_misr = readl_relaxed(base + GICH_MISR);
cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
if (unlikely(nr_lr > 32)) {
- eisr1 = readl_relaxed(base + GICH_EISR1);
elrsr1 = readl_relaxed(base + GICH_ELRSR1);
} else {
- eisr1 = elrsr1 = 0;
+ elrsr1 = 0;
}
#ifdef CONFIG_CPU_BIG_ENDIAN
- cpu_if->vgic_eisr = ((u64)eisr0 << 32) | eisr1;
cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
#else
- cpu_if->vgic_eisr = ((u64)eisr1 << 32) | eisr0;
cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
#endif
+ save_maint_int_state(vcpu, base);
+
for (i = 0; i < nr_lr; i++)
if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 03/12] KVM: arm/arm64: vgic-v2: Move GICH_ELRSR saving to its own function
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 01/12] KVM: arm/arm64: vgic-v2: Avoid accessing GICH registers Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 02/12] KVM: arm/arm64: vgic-v2: Save maintenance interrupt state only if required Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 04/12] KVM: arm/arm64: vgic-v2: Do not save an LR known to be empty Marc Zyngier
` (8 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
In order to make the saving path slightly more readable and
prepare for some more optimizations, let's move the GICH_ELRSR
saving to its own function.
No functional change.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/hyp/vgic-v2-sr.c | 36 +++++++++++++++++++++---------------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index 0dd83d5..c576228 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -64,6 +64,25 @@ static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu,
#endif
}
+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 = vcpu->arch.vgic_cpu.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
+}
+
/* vcpu is already in the HYP VA space */
void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
{
@@ -71,7 +90,6 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
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);
- u32 elrsr0, elrsr1;
int i, nr_lr;
if (!base)
@@ -81,22 +99,10 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
if (vcpu->arch.vgic_cpu.live_lrs) {
- elrsr0 = readl_relaxed(base + GICH_ELRSR0);
- cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
-
- 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
+ cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
save_maint_int_state(vcpu, base);
+ save_elrsr(vcpu, base);
for (i = 0; i < nr_lr; i++)
if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 04/12] KVM: arm/arm64: vgic-v2: Do not save an LR known to be empty
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (2 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 03/12] KVM: arm/arm64: vgic-v2: Move GICH_ELRSR saving to its own function Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 05/12] KVM: arm/arm64: vgic-v2: Reset LRs at boot time Marc Zyngier
` (7 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
On exit, any empty LR will be signaled in GICH_ELRSR*. Which
means that we do not have to save it, and we can just clear
its state in the in-memory copy.
Take this opportunity to move the LR saving code into its
own function.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/hyp/vgic-v2-sr.c | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index c576228..6d4dd78 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -83,6 +83,25 @@ static void __hyp_text save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
#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 nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+ int i;
+
+ for (i = 0; i < nr_lr; i++) {
+ if (!(vcpu->arch.vgic_cpu.live_lrs & (1UL << i)))
+ continue;
+
+ if (cpu_if->vgic_elrsr & (1UL << i)) {
+ cpu_if->vgic_lr[i] &= ~GICH_LR_STATE;
+ continue;
+ }
+
+ cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
+ }
+}
+
/* vcpu is already in the HYP VA space */
void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
{
@@ -90,12 +109,10 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
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, nr_lr;
if (!base)
return;
- nr_lr = vcpu->arch.vgic_cpu.nr_lr;
cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
if (vcpu->arch.vgic_cpu.live_lrs) {
@@ -103,10 +120,7 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
save_maint_int_state(vcpu, base);
save_elrsr(vcpu, base);
-
- for (i = 0; i < nr_lr; i++)
- if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
- cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
+ save_lrs(vcpu, base);
writel_relaxed(0, base + GICH_HCR);
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 05/12] KVM: arm/arm64: vgic-v2: Reset LRs at boot time
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (3 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 04/12] KVM: arm/arm64: vgic-v2: Do not save an LR known to be empty Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-09 3:07 ` Christoffer Dall
2016-03-07 9:33 ` [PATCH v3 06/12] KVM: arm/arm64: vgic-v2: Only wipe LRs on vcpu exit Marc Zyngier
` (6 subsequent siblings)
11 siblings, 1 reply; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
In order to let make the GICv2 code more lazy in the way it
accesses the LRs, it is necessary to start with a clean slate.
Let's reset the LRs on each CPU when the vgic is probed.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/vgic-v2.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
index ff02f08..67ec334 100644
--- a/virt/kvm/arm/vgic-v2.c
+++ b/virt/kvm/arm/vgic-v2.c
@@ -176,6 +176,15 @@ static const struct vgic_ops vgic_v2_ops = {
static struct vgic_params vgic_v2_params;
+static void vgic_cpu_init_lrs(void *params)
+{
+ struct vgic_params *vgic = params;
+ int i;
+
+ for (i = 0; i < vgic->nr_lr; i++)
+ writel_relaxed(0, vgic->vctrl_base + GICH_LR0 + (i * 4));
+}
+
/**
* vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
* @node: pointer to the DT node
@@ -257,6 +266,9 @@ int vgic_v2_probe(struct device_node *vgic_node,
vgic->type = VGIC_V2;
vgic->max_gic_vcpus = VGIC_V2_MAX_CPUS;
+
+ on_each_cpu(vgic_cpu_init_lrs, vgic, 1);
+
*ops = &vgic_v2_ops;
*params = vgic;
goto out;
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 05/12] KVM: arm/arm64: vgic-v2: Reset LRs at boot time
2016-03-07 9:33 ` [PATCH v3 05/12] KVM: arm/arm64: vgic-v2: Reset LRs at boot time Marc Zyngier
@ 2016-03-09 3:07 ` Christoffer Dall
0 siblings, 0 replies; 16+ messages in thread
From: Christoffer Dall @ 2016-03-09 3:07 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Mar 07, 2016 at 09:33:27AM +0000, Marc Zyngier wrote:
> In order to let make the GICv2 code more lazy in the way it
> accesses the LRs, it is necessary to start with a clean slate.
>
> Let's reset the LRs on each CPU when the vgic is probed.
>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
> ---
> virt/kvm/arm/vgic-v2.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
> index ff02f08..67ec334 100644
> --- a/virt/kvm/arm/vgic-v2.c
> +++ b/virt/kvm/arm/vgic-v2.c
> @@ -176,6 +176,15 @@ static const struct vgic_ops vgic_v2_ops = {
>
> static struct vgic_params vgic_v2_params;
>
> +static void vgic_cpu_init_lrs(void *params)
> +{
> + struct vgic_params *vgic = params;
> + int i;
> +
> + for (i = 0; i < vgic->nr_lr; i++)
> + writel_relaxed(0, vgic->vctrl_base + GICH_LR0 + (i * 4));
> +}
> +
> /**
> * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
> * @node: pointer to the DT node
> @@ -257,6 +266,9 @@ int vgic_v2_probe(struct device_node *vgic_node,
>
> vgic->type = VGIC_V2;
> vgic->max_gic_vcpus = VGIC_V2_MAX_CPUS;
> +
> + on_each_cpu(vgic_cpu_init_lrs, vgic, 1);
> +
> *ops = &vgic_v2_ops;
> *params = vgic;
> goto out;
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v3 06/12] KVM: arm/arm64: vgic-v2: Only wipe LRs on vcpu exit
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (4 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 05/12] KVM: arm/arm64: vgic-v2: Reset LRs at boot time Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-09 3:09 ` Christoffer Dall
2016-03-07 9:33 ` [PATCH v3 07/12] KVM: arm/arm64: vgic-v2: Make GICD_SGIR quicker to hit Marc Zyngier
` (5 subsequent siblings)
11 siblings, 1 reply; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
So far, we're always writing all possible LRs, setting the empty
ones with a zero value. This is obvious doing a lot of work for
nothing, and we're better off clearing those we've actually
dirtied on the exit path (it is very rare to inject more than one
interrupt at a time anyway).
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/hyp/vgic-v2-sr.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index 6d4dd78..674bdf8 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -99,6 +99,7 @@ static void __hyp_text save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
}
cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
+ writel_relaxed(0, base + GICH_LR0 + (i * 4));
}
}
@@ -156,12 +157,11 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
for (i = 0; i < nr_lr; i++) {
- u32 val = 0;
-
- if (live_lrs & (1UL << i))
- val = cpu_if->vgic_lr[i];
+ if (!(live_lrs & (1UL << i)))
+ continue;
- writel_relaxed(val, base + GICH_LR0 + (i * 4));
+ writel_relaxed(cpu_if->vgic_lr[i],
+ base + GICH_LR0 + (i * 4));
}
}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 06/12] KVM: arm/arm64: vgic-v2: Only wipe LRs on vcpu exit
2016-03-07 9:33 ` [PATCH v3 06/12] KVM: arm/arm64: vgic-v2: Only wipe LRs on vcpu exit Marc Zyngier
@ 2016-03-09 3:09 ` Christoffer Dall
0 siblings, 0 replies; 16+ messages in thread
From: Christoffer Dall @ 2016-03-09 3:09 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Mar 07, 2016 at 09:33:28AM +0000, Marc Zyngier wrote:
> So far, we're always writing all possible LRs, setting the empty
> ones with a zero value. This is obvious doing a lot of work for
> nothing, and we're better off clearing those we've actually
> dirtied on the exit path (it is very rare to inject more than one
> interrupt at a time anyway).
>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
> ---
> virt/kvm/arm/hyp/vgic-v2-sr.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
> index 6d4dd78..674bdf8 100644
> --- a/virt/kvm/arm/hyp/vgic-v2-sr.c
> +++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
> @@ -99,6 +99,7 @@ static void __hyp_text save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
> }
>
> cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
> + writel_relaxed(0, base + GICH_LR0 + (i * 4));
> }
> }
>
> @@ -156,12 +157,11 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
> writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
> writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
> for (i = 0; i < nr_lr; i++) {
> - u32 val = 0;
> -
> - if (live_lrs & (1UL << i))
> - val = cpu_if->vgic_lr[i];
> + if (!(live_lrs & (1UL << i)))
> + continue;
>
> - writel_relaxed(val, base + GICH_LR0 + (i * 4));
> + writel_relaxed(cpu_if->vgic_lr[i],
> + base + GICH_LR0 + (i * 4));
> }
> }
>
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v3 07/12] KVM: arm/arm64: vgic-v2: Make GICD_SGIR quicker to hit
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (5 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 06/12] KVM: arm/arm64: vgic-v2: Only wipe LRs on vcpu exit Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 08/12] arm64: KVM: vgic-v3: Avoid accessing ICH registers Marc Zyngier
` (4 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
The GICD_SGIR register lives a long way from the beginning of
the handler array, which is searched linearly. As this is hit
pretty often, let's move it up. This saves us some precious
cycles when the guest is generating IPIs.
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/vgic-v2-emul.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/virt/kvm/arm/vgic-v2-emul.c b/virt/kvm/arm/vgic-v2-emul.c
index 1390797..1b0bee0 100644
--- a/virt/kvm/arm/vgic-v2-emul.c
+++ b/virt/kvm/arm/vgic-v2-emul.c
@@ -321,6 +321,11 @@ static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu,
static const struct vgic_io_range vgic_dist_ranges[] = {
{
+ .base = GIC_DIST_SOFTINT,
+ .len = 4,
+ .handle_mmio = handle_mmio_sgi_reg,
+ },
+ {
.base = GIC_DIST_CTRL,
.len = 12,
.bits_per_irq = 0,
@@ -387,11 +392,6 @@ static const struct vgic_io_range vgic_dist_ranges[] = {
.handle_mmio = handle_mmio_cfg_reg,
},
{
- .base = GIC_DIST_SOFTINT,
- .len = 4,
- .handle_mmio = handle_mmio_sgi_reg,
- },
- {
.base = GIC_DIST_SGI_PENDING_CLEAR,
.len = VGIC_NR_SGIS,
.handle_mmio = handle_mmio_sgi_clear,
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 08/12] arm64: KVM: vgic-v3: Avoid accessing ICH registers
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (6 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 07/12] KVM: arm/arm64: vgic-v2: Make GICD_SGIR quicker to hit Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 09/12] arm64: KVM: vgic-v3: Save maintenance interrupt state only if required Marc Zyngier
` (3 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
Just like on GICv2, we're a bit hammer-happy with GICv3, and access
them more often than we should.
Adopt a policy similar to what we do for GICv2, only save/restoring
the minimal set of registers. As we don't access the registers
linearly anymore (we may skip some), the convoluted accessors become
slightly simpler, and we can drop the ugly indexing macro that
tended to confuse the reviewers.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/kvm/hyp/vgic-v3-sr.c | 293 ++++++++++++++++++++++++----------------
include/kvm/arm_vgic.h | 6 -
virt/kvm/arm/vgic-v3.c | 4 +-
3 files changed, 182 insertions(+), 121 deletions(-)
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 0035b2d..e596945 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -37,12 +37,104 @@
asm volatile("msr_s " __stringify(r) ", %0" : : "r" (__val));\
} while (0)
-/* vcpu is already in the HYP VA space */
+static u64 __hyp_text __gic_v3_get_lr(unsigned int lr)
+{
+ switch (lr & 0xf) {
+ case 0:
+ return read_gicreg(ICH_LR0_EL2);
+ case 1:
+ return read_gicreg(ICH_LR1_EL2);
+ case 2:
+ return read_gicreg(ICH_LR2_EL2);
+ case 3:
+ return read_gicreg(ICH_LR3_EL2);
+ case 4:
+ return read_gicreg(ICH_LR4_EL2);
+ case 5:
+ return read_gicreg(ICH_LR5_EL2);
+ case 6:
+ return read_gicreg(ICH_LR6_EL2);
+ case 7:
+ return read_gicreg(ICH_LR7_EL2);
+ case 8:
+ return read_gicreg(ICH_LR8_EL2);
+ case 9:
+ return read_gicreg(ICH_LR9_EL2);
+ case 10:
+ return read_gicreg(ICH_LR10_EL2);
+ case 11:
+ return read_gicreg(ICH_LR11_EL2);
+ case 12:
+ return read_gicreg(ICH_LR12_EL2);
+ case 13:
+ return read_gicreg(ICH_LR13_EL2);
+ case 14:
+ return read_gicreg(ICH_LR14_EL2);
+ case 15:
+ return read_gicreg(ICH_LR15_EL2);
+ }
+
+ unreachable();
+}
+
+static void __hyp_text __gic_v3_set_lr(u64 val, int lr)
+{
+ switch (lr & 0xf) {
+ case 0:
+ write_gicreg(val, ICH_LR0_EL2);
+ break;
+ case 1:
+ write_gicreg(val, ICH_LR1_EL2);
+ break;
+ case 2:
+ write_gicreg(val, ICH_LR2_EL2);
+ break;
+ case 3:
+ write_gicreg(val, ICH_LR3_EL2);
+ break;
+ case 4:
+ write_gicreg(val, ICH_LR4_EL2);
+ break;
+ case 5:
+ write_gicreg(val, ICH_LR5_EL2);
+ break;
+ case 6:
+ write_gicreg(val, ICH_LR6_EL2);
+ break;
+ case 7:
+ write_gicreg(val, ICH_LR7_EL2);
+ break;
+ case 8:
+ write_gicreg(val, ICH_LR8_EL2);
+ break;
+ case 9:
+ write_gicreg(val, ICH_LR9_EL2);
+ break;
+ case 10:
+ write_gicreg(val, ICH_LR10_EL2);
+ break;
+ case 11:
+ write_gicreg(val, ICH_LR11_EL2);
+ break;
+ case 12:
+ write_gicreg(val, ICH_LR12_EL2);
+ break;
+ case 13:
+ write_gicreg(val, ICH_LR13_EL2);
+ break;
+ case 14:
+ write_gicreg(val, ICH_LR14_EL2);
+ break;
+ case 15:
+ write_gicreg(val, ICH_LR15_EL2);
+ break;
+ }
+}
+
void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
{
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
u64 val;
- u32 max_lr_idx, nr_pri_bits;
/*
* Make sure stores to the GIC via the memory mapped interface
@@ -51,68 +143,58 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
dsb(st);
cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
- cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
- cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
- cpu_if->vgic_elrsr = read_gicreg(ICH_ELSR_EL2);
- write_gicreg(0, ICH_HCR_EL2);
- val = read_gicreg(ICH_VTR_EL2);
- max_lr_idx = vtr_to_max_lr_idx(val);
- nr_pri_bits = vtr_to_nr_pri_bits(val);
+ if (vcpu->arch.vgic_cpu.live_lrs) {
+ int i;
+ u32 max_lr_idx, nr_pri_bits;
- switch (max_lr_idx) {
- case 15:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(15)] = read_gicreg(ICH_LR15_EL2);
- case 14:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(14)] = read_gicreg(ICH_LR14_EL2);
- case 13:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(13)] = read_gicreg(ICH_LR13_EL2);
- case 12:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(12)] = read_gicreg(ICH_LR12_EL2);
- case 11:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(11)] = read_gicreg(ICH_LR11_EL2);
- case 10:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(10)] = read_gicreg(ICH_LR10_EL2);
- case 9:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(9)] = read_gicreg(ICH_LR9_EL2);
- case 8:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(8)] = read_gicreg(ICH_LR8_EL2);
- case 7:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(7)] = read_gicreg(ICH_LR7_EL2);
- case 6:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(6)] = read_gicreg(ICH_LR6_EL2);
- case 5:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(5)] = read_gicreg(ICH_LR5_EL2);
- case 4:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(4)] = read_gicreg(ICH_LR4_EL2);
- case 3:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(3)] = read_gicreg(ICH_LR3_EL2);
- case 2:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(2)] = read_gicreg(ICH_LR2_EL2);
- case 1:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(1)] = read_gicreg(ICH_LR1_EL2);
- case 0:
- cpu_if->vgic_lr[VGIC_V3_LR_INDEX(0)] = read_gicreg(ICH_LR0_EL2);
- }
+ cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
+ cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
+ cpu_if->vgic_elrsr = read_gicreg(ICH_ELSR_EL2);
- switch (nr_pri_bits) {
- case 7:
- cpu_if->vgic_ap0r[3] = read_gicreg(ICH_AP0R3_EL2);
- cpu_if->vgic_ap0r[2] = read_gicreg(ICH_AP0R2_EL2);
- case 6:
- cpu_if->vgic_ap0r[1] = read_gicreg(ICH_AP0R1_EL2);
- default:
- cpu_if->vgic_ap0r[0] = read_gicreg(ICH_AP0R0_EL2);
- }
+ write_gicreg(0, ICH_HCR_EL2);
+ val = read_gicreg(ICH_VTR_EL2);
+ max_lr_idx = vtr_to_max_lr_idx(val);
+ nr_pri_bits = vtr_to_nr_pri_bits(val);
- switch (nr_pri_bits) {
- case 7:
- cpu_if->vgic_ap1r[3] = read_gicreg(ICH_AP1R3_EL2);
- cpu_if->vgic_ap1r[2] = read_gicreg(ICH_AP1R2_EL2);
- case 6:
- cpu_if->vgic_ap1r[1] = read_gicreg(ICH_AP1R1_EL2);
- default:
- cpu_if->vgic_ap1r[0] = read_gicreg(ICH_AP1R0_EL2);
+ for (i = 0; i <= max_lr_idx; i++) {
+ if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
+ cpu_if->vgic_lr[i] = __gic_v3_get_lr(i);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ cpu_if->vgic_ap0r[3] = read_gicreg(ICH_AP0R3_EL2);
+ cpu_if->vgic_ap0r[2] = read_gicreg(ICH_AP0R2_EL2);
+ case 6:
+ cpu_if->vgic_ap0r[1] = read_gicreg(ICH_AP0R1_EL2);
+ default:
+ cpu_if->vgic_ap0r[0] = read_gicreg(ICH_AP0R0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ cpu_if->vgic_ap1r[3] = read_gicreg(ICH_AP1R3_EL2);
+ cpu_if->vgic_ap1r[2] = read_gicreg(ICH_AP1R2_EL2);
+ case 6:
+ cpu_if->vgic_ap1r[1] = read_gicreg(ICH_AP1R1_EL2);
+ default:
+ cpu_if->vgic_ap1r[0] = read_gicreg(ICH_AP1R0_EL2);
+ }
+
+ vcpu->arch.vgic_cpu.live_lrs = 0;
+ } else {
+ cpu_if->vgic_misr = 0;
+ cpu_if->vgic_eisr = 0;
+ cpu_if->vgic_elrsr = 0xffff;
+ cpu_if->vgic_ap0r[0] = 0;
+ cpu_if->vgic_ap0r[1] = 0;
+ cpu_if->vgic_ap0r[2] = 0;
+ cpu_if->vgic_ap0r[3] = 0;
+ cpu_if->vgic_ap1r[0] = 0;
+ cpu_if->vgic_ap1r[1] = 0;
+ cpu_if->vgic_ap1r[2] = 0;
+ cpu_if->vgic_ap1r[3] = 0;
}
val = read_gicreg(ICC_SRE_EL2);
@@ -126,6 +208,8 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
u64 val;
u32 max_lr_idx, nr_pri_bits;
+ u16 live_lrs = 0;
+ int i;
/*
* VFIQEn is RES1 if ICC_SRE_EL1.SRE is 1. This causes a
@@ -138,66 +222,48 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
write_gicreg(cpu_if->vgic_sre, ICC_SRE_EL1);
isb();
- write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
- write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
-
val = read_gicreg(ICH_VTR_EL2);
max_lr_idx = vtr_to_max_lr_idx(val);
nr_pri_bits = vtr_to_nr_pri_bits(val);
- switch (nr_pri_bits) {
- case 7:
- write_gicreg(cpu_if->vgic_ap0r[3], ICH_AP0R3_EL2);
- write_gicreg(cpu_if->vgic_ap0r[2], ICH_AP0R2_EL2);
- case 6:
- write_gicreg(cpu_if->vgic_ap0r[1], ICH_AP0R1_EL2);
- default:
- write_gicreg(cpu_if->vgic_ap0r[0], ICH_AP0R0_EL2);
+ for (i = 0; i <= max_lr_idx; i++) {
+ if (cpu_if->vgic_lr[i] & ICH_LR_STATE)
+ live_lrs |= (1 << i);
}
- switch (nr_pri_bits) {
- case 7:
- write_gicreg(cpu_if->vgic_ap1r[3], ICH_AP1R3_EL2);
- write_gicreg(cpu_if->vgic_ap1r[2], ICH_AP1R2_EL2);
- case 6:
- write_gicreg(cpu_if->vgic_ap1r[1], ICH_AP1R1_EL2);
- default:
- write_gicreg(cpu_if->vgic_ap1r[0], ICH_AP1R0_EL2);
- }
+ write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
- switch (max_lr_idx) {
- case 15:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(15)], ICH_LR15_EL2);
- case 14:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(14)], ICH_LR14_EL2);
- case 13:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(13)], ICH_LR13_EL2);
- case 12:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(12)], ICH_LR12_EL2);
- case 11:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(11)], ICH_LR11_EL2);
- case 10:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(10)], ICH_LR10_EL2);
- case 9:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(9)], ICH_LR9_EL2);
- case 8:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(8)], ICH_LR8_EL2);
- case 7:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(7)], ICH_LR7_EL2);
- case 6:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(6)], ICH_LR6_EL2);
- case 5:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(5)], ICH_LR5_EL2);
- case 4:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(4)], ICH_LR4_EL2);
- case 3:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(3)], ICH_LR3_EL2);
- case 2:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(2)], ICH_LR2_EL2);
- case 1:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(1)], ICH_LR1_EL2);
- case 0:
- write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(0)], ICH_LR0_EL2);
+ if (live_lrs) {
+ write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
+
+ switch (nr_pri_bits) {
+ case 7:
+ write_gicreg(cpu_if->vgic_ap0r[3], ICH_AP0R3_EL2);
+ write_gicreg(cpu_if->vgic_ap0r[2], ICH_AP0R2_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_ap0r[1], ICH_AP0R1_EL2);
+ default:
+ write_gicreg(cpu_if->vgic_ap0r[0], ICH_AP0R0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ write_gicreg(cpu_if->vgic_ap1r[3], ICH_AP1R3_EL2);
+ write_gicreg(cpu_if->vgic_ap1r[2], ICH_AP1R2_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_ap1r[1], ICH_AP1R1_EL2);
+ default:
+ write_gicreg(cpu_if->vgic_ap1r[0], ICH_AP1R0_EL2);
+ }
+
+ for (i = 0; i <= max_lr_idx; i++) {
+ val = 0;
+
+ if (live_lrs & (1 << i))
+ val = cpu_if->vgic_lr[i];
+
+ __gic_v3_set_lr(val, i);
+ }
}
/*
@@ -207,6 +273,7 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
*/
isb();
dsb(sy);
+ vcpu->arch.vgic_cpu.live_lrs = live_lrs;
/*
* Prevent the guest from touching the GIC system registers if
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index f473fd6..281caf8 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -279,12 +279,6 @@ struct vgic_v2_cpu_if {
u32 vgic_lr[VGIC_V2_MAX_LRS];
};
-/*
- * LRs are stored in reverse order in memory. make sure we index them
- * correctly.
- */
-#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
-
struct vgic_v3_cpu_if {
#ifdef CONFIG_KVM_ARM_VGIC_V3
u32 vgic_hcr;
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 453eafd..11b5ff6 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -42,7 +42,7 @@ static u32 ich_vtr_el2;
static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr)
{
struct vgic_lr lr_desc;
- u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[VGIC_V3_LR_INDEX(lr)];
+ u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr];
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
lr_desc.irq = val & ICH_LR_VIRTUALID_MASK;
@@ -106,7 +106,7 @@ static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
lr_val |= ((u64)lr_desc.hwirq) << ICH_LR_PHYS_ID_SHIFT;
}
- vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[VGIC_V3_LR_INDEX(lr)] = lr_val;
+ vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = lr_val;
if (!(lr_desc.state & LR_STATE_MASK))
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 09/12] arm64: KVM: vgic-v3: Save maintenance interrupt state only if required
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (7 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 08/12] arm64: KVM: vgic-v3: Avoid accessing ICH registers Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 10/12] arm64: KVM: vgic-v3: Do not save an LR known to be empty Marc Zyngier
` (2 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
Next on our list of useless accesses is the maintenance interrupt
status registers (ICH_MISR_EL2, ICH_EISR_EL2).
It is pointless to save them if we haven't asked for a maintenance
interrupt the first place, which can only happen for two reasons:
- Underflow: ICH_HCR_UIE will be set,
- EOI: ICH_LR_EOI will be set.
These conditions can be checked on the in-memory copies of the regs.
Should any of these two condition be valid, we must read GICH_MISR.
We can then check for ICH_MISR_EOI, and only when set read
ICH_EISR_EL2.
This means that in most case, we don't have to save them at all.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/kvm/hyp/vgic-v3-sr.c | 33 +++++++++++++++++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index e596945..61a5e46 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -131,6 +131,35 @@ static void __hyp_text __gic_v3_set_lr(u64 val, int lr)
}
}
+static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu, int nr_lr)
+{
+ struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
+ int i;
+ bool expect_mi;
+
+ expect_mi = !!(cpu_if->vgic_hcr & ICH_HCR_UIE);
+
+ for (i = 0; i < nr_lr; i++) {
+ if (!(vcpu->arch.vgic_cpu.live_lrs & (1UL << i)))
+ continue;
+
+ expect_mi |= (!(cpu_if->vgic_lr[i] & ICH_LR_HW) &&
+ (cpu_if->vgic_lr[i] & ICH_LR_EOI));
+ }
+
+ if (expect_mi) {
+ cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
+
+ if (cpu_if->vgic_misr & ICH_MISR_EOI)
+ cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
+ else
+ cpu_if->vgic_eisr = 0;
+ } else {
+ cpu_if->vgic_misr = 0;
+ cpu_if->vgic_eisr = 0;
+ }
+}
+
void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
{
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
@@ -148,8 +177,6 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
int i;
u32 max_lr_idx, nr_pri_bits;
- cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
- cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
cpu_if->vgic_elrsr = read_gicreg(ICH_ELSR_EL2);
write_gicreg(0, ICH_HCR_EL2);
@@ -157,6 +184,8 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
max_lr_idx = vtr_to_max_lr_idx(val);
nr_pri_bits = vtr_to_nr_pri_bits(val);
+ save_maint_int_state(vcpu, max_lr_idx + 1);
+
for (i = 0; i <= max_lr_idx; i++) {
if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
cpu_if->vgic_lr[i] = __gic_v3_get_lr(i);
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 10/12] arm64: KVM: vgic-v3: Do not save an LR known to be empty
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (8 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 09/12] arm64: KVM: vgic-v3: Save maintenance interrupt state only if required Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 11/12] arm64: KVM: vgic-v3: Reset LRs at boot time Marc Zyngier
2016-03-07 9:33 ` [PATCH v3 12/12] arm64: KVM: vgic-v3: Only wipe LRs on vcpu exit Marc Zyngier
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
On exit, any empty LR will be signaled in ICH_ELRSR_EL2. Which
means that we do not have to save it, and we can just clear
its state in the in-memory copy.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/kvm/hyp/vgic-v3-sr.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 61a5e46..0db426e 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -187,8 +187,15 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
save_maint_int_state(vcpu, max_lr_idx + 1);
for (i = 0; i <= max_lr_idx; i++) {
- if (vcpu->arch.vgic_cpu.live_lrs & (1UL << i))
- cpu_if->vgic_lr[i] = __gic_v3_get_lr(i);
+ if (!(vcpu->arch.vgic_cpu.live_lrs & (1UL << i)))
+ continue;
+
+ if (cpu_if->vgic_elrsr & (1 << i)) {
+ cpu_if->vgic_lr[i] &= ~ICH_LR_STATE;
+ continue;
+ }
+
+ cpu_if->vgic_lr[i] = __gic_v3_get_lr(i);
}
switch (nr_pri_bits) {
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 11/12] arm64: KVM: vgic-v3: Reset LRs at boot time
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (9 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 10/12] arm64: KVM: vgic-v3: Do not save an LR known to be empty Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
2016-03-09 3:12 ` Christoffer Dall
2016-03-07 9:33 ` [PATCH v3 12/12] arm64: KVM: vgic-v3: Only wipe LRs on vcpu exit Marc Zyngier
11 siblings, 1 reply; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
In order to let the GICv3 code be more lazy in the way it
accesses the LRs, it is necessary to start with a clean slate.
Let's reset the LRs on each CPU when the vgic is probed (which
includes a round trip to EL2...).
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/kvm/hyp/vgic-v3-sr.c | 9 +++++++++
virt/kvm/arm/vgic-v3.c | 7 +++++++
3 files changed, 17 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 1037392..2d02ba6 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -42,6 +42,7 @@ extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
extern u64 __vgic_v3_get_ich_vtr_el2(void);
+extern void __vgic_v3_init_lrs(void);
extern u32 __kvm_get_mdcr_el2(void);
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 0db426e..8134947 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -321,6 +321,15 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
}
}
+void __hyp_text __vgic_v3_init_lrs(void)
+{
+ int max_lr_idx = vtr_to_max_lr_idx(read_gicreg(ICH_VTR_EL2));
+ int i;
+
+ for (i = 0; i <= max_lr_idx; i++)
+ __gic_v3_set_lr(0, i);
+}
+
static u64 __hyp_text __vgic_v3_read_ich_vtr_el2(void)
{
return read_gicreg(ICH_VTR_EL2);
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 11b5ff6..999bdc6 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -216,6 +216,11 @@ static const struct vgic_ops vgic_v3_ops = {
static struct vgic_params vgic_v3_params;
+static void vgic_cpu_init_lrs(void *params)
+{
+ kvm_call_hyp(__vgic_v3_init_lrs);
+}
+
/**
* vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
* @node: pointer to the DT node
@@ -284,6 +289,8 @@ int vgic_v3_probe(struct device_node *vgic_node,
kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
vcpu_res.start, vgic->maint_irq);
+ on_each_cpu(vgic_cpu_init_lrs, vgic, 1);
+
*ops = &vgic_v3_ops;
*params = vgic;
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3 12/12] arm64: KVM: vgic-v3: Only wipe LRs on vcpu exit
2016-03-07 9:33 [PATCH v3 00/12] Virtual GIC save/restore optimization Marc Zyngier
` (10 preceding siblings ...)
2016-03-07 9:33 ` [PATCH v3 11/12] arm64: KVM: vgic-v3: Reset LRs at boot time Marc Zyngier
@ 2016-03-07 9:33 ` Marc Zyngier
11 siblings, 0 replies; 16+ messages in thread
From: Marc Zyngier @ 2016-03-07 9:33 UTC (permalink / raw)
To: linux-arm-kernel
So far, we're always writing all possible LRs, setting the empty
ones with a zero value. This is obvious doing a low of work for
nothing, and we're better off clearing those we've actually
dirtied on the exit path (it is very rare to inject more than one
interrupt at a time anyway).
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/kvm/hyp/vgic-v3-sr.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 8134947..fff7cd4 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -196,6 +196,7 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
}
cpu_if->vgic_lr[i] = __gic_v3_get_lr(i);
+ __gic_v3_set_lr(0, i);
}
switch (nr_pri_bits) {
@@ -293,12 +294,10 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
}
for (i = 0; i <= max_lr_idx; i++) {
- val = 0;
-
- if (live_lrs & (1 << i))
- val = cpu_if->vgic_lr[i];
+ if (!(live_lrs & (1 << i)))
+ continue;
- __gic_v3_set_lr(val, i);
+ __gic_v3_set_lr(cpu_if->vgic_lr[i], i);
}
}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread