e9354b5825
1 OBS-URL: https://build.opensuse.org/request/show/483217 OBS-URL: https://build.opensuse.org/package/show/Virtualization/qemu?expand=0&rev=331
237 lines
7.8 KiB
Diff
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
|