SHA256
1
0
forked from pool/qemu
qemu/0041-ARM-KVM-Enable-in-kernel-timers-wit.patch

237 lines
7.8 KiB
Diff

From afc51239827b010f266a7411981dd3c99014ea49 Mon Sep 17 00:00:00 2001
From: Alexander Graf <agraf@suse.de>
Date: Mon, 19 Sep 2016 10:02:55 +0200
Subject: [PATCH] ARM: KVM: Enable in-kernel timers with user space gic
When running with KVM enabled, you can choose between emulating the
gic in kernel or user space. If the kernel supports in-kernel virtualization
of the interrupt controller, it will default to that. If not, if will
default to user space emulation.
Unfortunately when running in user mode gic emulation, we miss out on
timer events which are only available from kernel space. This patch leverages
the new kernel/user space notification mechanism for those timer events.
Signed-off-by: Alexander Graf <agraf@suse.de>
---
hw/arm/virt.c | 18 ++++++++++++++++++
hw/intc/Makefile.objs | 2 +-
hw/intc/arm_gic.c | 16 ++++++++++++++++
linux-headers/linux/kvm.h | 14 ++++++++++++++
target/arm/kvm.c | 29 ++++++++++++++++++++++++++++-
target/arm/kvm_arm.h | 11 +++++++++++
6 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 5f62a03..44e1170 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -609,6 +609,24 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
} else if (type == 2) {
create_v2m(vms, pic);
}
+
+#ifdef CONFIG_KVM
+ if (kvm_enabled() && !kvm_irqchip_in_kernel()) {
+ for (i = 0; i < smp_cpus; i++) {
+ CPUState *cs = qemu_get_cpu(i);
+ int ret;
+
+ ret = kvm_vcpu_enable_cap(cs, KVM_CAP_ARM_TIMER, 0,
+ KVM_ARM_TIMER_VTIMER);
+
+ if (ret) {
+ error_report("KVM with user space irqchip only works when the "
+ "host kernel supports KVM_CAP_ARM_TIMER");
+ exit(1);
+ }
+ }
+ }
+#endif
}
static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index adedd0d..6434f2c 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -10,7 +10,6 @@ common-obj-$(CONFIG_REALVIEW) += realview_gic.o
common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o
common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
-common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3.o
@@ -21,6 +20,7 @@ common-obj-$(CONFIG_OPENPIC) += openpic.o
common-obj-y += intc.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
+obj-$(CONFIG_ARM_GIC) += arm_gic.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o
obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_its_kvm.o
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index b305d90..396bfab 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -25,6 +25,7 @@
#include "qom/cpu.h"
#include "qemu/log.h"
#include "trace.h"
+#include "kvm_arm.h"
/* #define DEBUG_GIC */
@@ -549,6 +550,11 @@ static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
return;
}
+ /* Tell KVM that we want to know about timer IRQs again */
+ if (kvm_enabled()) {
+ kvm_arm_eoi_notify(cpu);
+ }
+
GIC_CLEAR_ACTIVE(irq, cm);
}
@@ -558,6 +564,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
int group;
DPRINTF("EOI %d\n", irq);
+
+ /* Tell KVM that we want to know about timer IRQs again */
+ if (kvm_enabled()) {
+ kvm_arm_eoi_notify(cpu);
+ }
+
if (irq >= s->num_irq) {
/* This handles two cases:
* 1. If software writes the ID of a spurious interrupt [ie 1023]
@@ -899,6 +911,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
trace_gic_enable_irq(irq + i);
}
GIC_SET_ENABLED(irq + i, cm);
+ /* Tell KVM that we want to know about timer IRQs again */
+ if (kvm_enabled()) {
+ kvm_arm_eoi_notify(cpu);
+ }
/* If a raised level triggered IRQ enabled then mark
is as pending. */
if (GIC_TEST_LEVEL(irq + i, mask)
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 4e082a8..41b6296 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -205,6 +205,7 @@ struct kvm_hyperv_exit {
#define KVM_EXIT_S390_STSI 25
#define KVM_EXIT_IOAPIC_EOI 26
#define KVM_EXIT_HYPERV 27
+#define KVM_EXIT_ARM_TIMER 28
/* For KVM_EXIT_INTERNAL_ERROR */
/* Emulate instruction failed. */
@@ -362,6 +363,10 @@ struct kvm_run {
} eoi;
/* KVM_EXIT_HYPERV */
struct kvm_hyperv_exit hyperv;
+ /* KVM_EXIT_ARM_TIMER */
+ struct {
+ __u8 timesource;
+ } arm_timer;
/* Fix the size of the union. */
char padding[256];
};
@@ -883,6 +888,7 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_PPC_MMU_RADIX 134
#define KVM_CAP_PPC_MMU_HASH_V3 135
#define KVM_CAP_IMMEDIATE_EXIT 136
+#define KVM_CAP_ARM_TIMER 137
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1354,4 +1360,12 @@ struct kvm_assigned_msix_entry {
#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0)
#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1)
+/* Available with KVM_CAP_ARM_TIMER */
+
+/* Bits for run->request_interrupt_window */
+#define KVM_IRQWINDOW_VTIMER (1 << 0)
+
+/* Bits for run->arm_timer.timesource */
+#define KVM_ARM_TIMER_VTIMER (1 << 0)
+
#endif /* __LINUX_KVM_H */
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 4555468..15d157c 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -531,7 +531,6 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
return MEMTXATTRS_UNSPECIFIED;
}
-
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
{
int ret = 0;
@@ -542,6 +541,23 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
ret = EXCP_DEBUG;
} /* otherwise return to guest */
break;
+ case KVM_EXIT_ARM_TIMER:
+ /* We only support the vtimer today */
+ if (run->arm_timer.timesource != KVM_ARM_TIMER_VTIMER) {
+ return -EINVAL;
+ }
+
+ /*
+ * We ask the kernel to not tell us about pending virtual timer irqs,
+ * so that we can process the IRQ until we get an EOI for it. Once the
+ * EOI hits, we unset and unmask the interrupt again and if it is still
+ * pending, we set the line high again
+ */
+ run->request_interrupt_window = KVM_IRQWINDOW_VTIMER;
+
+ /* Internally trigger virtual timer IRQ */
+ qemu_set_irq(ARM_CPU(cs)->gt_timer_outputs[GTIMER_VIRT], 1);
+ break;
default:
qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n",
__func__, run->exit_reason);
@@ -629,3 +645,14 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
{
return (data - 32) & 0xffff;
}
+
+void kvm_arm_eoi_notify(int cpu)
+{
+ CPUState *cs;
+
+ cs = qemu_get_cpu(cpu);
+
+ /* Disable vtimer - if it's still pending we get notified again */
+ cs->kvm_run->request_interrupt_window &= ~KVM_ARM_TIMER_VTIMER;
+ qemu_set_irq(ARM_CPU(cs)->gt_timer_outputs[GTIMER_VIRT], 0);
+}
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 633d088..eeec8c5 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -288,4 +288,15 @@ static inline const char *its_class_name(void)
}
}
+/**
+ * kvm_arm_eoi_notify:
+ *
+ * @cpu: CPU index the EOI is for
+ *
+ * Notify KVM that we're done processing an interrupt. This is
+ * used to unmask any pending timer interrupts and potentially
+ * learn about the fact that the level is still high.
+ */
+void kvm_arm_eoi_notify(int cpu);
+
#endif