Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1547359 > unrolled thread
| Started by | Jintack Lim <jintack@cs.columbia.edu> |
|---|---|
| First post | 2016-12-26 18:50 +0100 |
| Last post | 2016-12-26 18:50 +0100 |
| Articles | 3 — 1 participant |
Back to article view | Back to linux.kernel
[RFC 0/8] Provide the EL1 physical timer to the VM Jintack Lim <jintack@cs.columbia.edu> - 2016-12-26 18:50 +0100
[RFC 1/8] KVM: arm/arm64: Abstract virtual timer context into separate structure Jintack Lim <jintack@cs.columbia.edu> - 2016-12-26 18:50 +0100
[RFC 5/8] KVM: arm64: Add the EL1 physical timer access handler Jintack Lim <jintack@cs.columbia.edu> - 2016-12-26 18:50 +0100
| From | Jintack Lim <jintack@cs.columbia.edu> |
|---|---|
| Date | 2016-12-26 18:50 +0100 |
| Subject | [RFC 0/8] Provide the EL1 physical timer to the VM |
| Message-ID | <sSHzj-7KX-3@gated-at.bofh.it> |
The ARM architecture defines the EL1 physical timer and the virtual
timer, and it is reasonable for an OS to expect to be able to access
both. However, the current KVM implementation does not provide the EL1
physical timer to VMs but terminates VMs on access to the timer.
On VHE systems, this would be as simple as allowing full access to the
EL1 physical timer to VMs because the KVM host does not use the EL1
physical timer. However, on non-VHE systems, the KVM host already uses
the EL1 physical timer which prevents us from granting full access of
the EL1 physical timer to VMs.
This patchset enables VMs to use the EL1 physical timer through
trap-and-emulate. The KVM host emulates each EL1 physical timer
register access and sets up the background timer accordingly. When the
background timer expires, the KVM host injects EL1 physical timer
interrupts to the VM. Alternatively, it's also possible to allow VMs to
access the EL1 physical timer without trapping. However, this requires
somehow using the EL2 physical timer for the Linux host while running
the VM instead of the EL1 physical timer. Right now I just implemented
trap-and-emulate because this was straightforward to do, and I leave it
to future work to determine if transferring the EL1 physical timer state
to the EL2 timer provides any performance benefit.
This feature will be useful for any OS that wishes to access the EL1
physical timer. Nested virtualization is one of those use cases. A
nested hypervisor running inside a VM would think it has full access to
the hardware and naturally tries to use the EL1 physical timer as Linux
would do. Other nested hypervisors may try to use the EL2 physical timer
as Xen would do, but supporting the EL2 physical timer to the VM is out
of scope of this patchset. This patchset will make it easy to add the
EL2 timer support in the future, though.
Note, Linux VMs booting in EL1 will be unaffected by this patch set and
will continue to use only the virtual timer and this patch set will
therefore not introduce any performance degredation as a result of
trap-and-emulate.
Jintack Lim (8):
KVM: arm/arm64: Abstract virtual timer context into separate structure
KVM: arm/arm64: Decouple kvm timer functions from virtual timer
KVM: arm/arm64: Add the EL1 physical timer context
KVM: arm/arm64: Initialize the emulated EL1 physical timer
KVM: arm64: Add the EL1 physical timer access handler
KVM: arm/arm64: Update the physical timer interrupt level
KVM: arm/arm64: Set up a background timer for the physical timer
emulation
KVM: arm/arm64: Emulate the EL1 phys timer register access
arch/arm/kvm/arm.c | 3 +-
arch/arm/kvm/reset.c | 9 +-
arch/arm64/kvm/reset.c | 9 +-
arch/arm64/kvm/sys_regs.c | 63 +++++++++++++
include/kvm/arm_arch_timer.h | 43 +++++----
virt/kvm/arm/arch_timer.c | 214 ++++++++++++++++++++++++++++++-------------
virt/kvm/arm/hyp/timer-sr.c | 16 ++--
7 files changed, 264 insertions(+), 93 deletions(-)
--
1.9.1
[toc] | [next] | [standalone]
| From | Jintack Lim <jintack@cs.columbia.edu> |
|---|---|
| Date | 2016-12-26 18:50 +0100 |
| Subject | [RFC 1/8] KVM: arm/arm64: Abstract virtual timer context into separate structure |
| Message-ID | <sSHzk-7KX-23@gated-at.bofh.it> |
| In reply to | #1547359 |
Abstract virtual timer context into a separate structure and change all
callers referring to timer registers, irq state and so on. No change in
functionality.
This is about to become very handy when adding the EL1 physical timer.
Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
include/kvm/arm_arch_timer.h | 32 +++++++++---------
virt/kvm/arm/arch_timer.c | 77 ++++++++++++++++++++++----------------------
virt/kvm/arm/hyp/timer-sr.c | 16 ++++-----
3 files changed, 63 insertions(+), 62 deletions(-)
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index dda39d8..7dabe56 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -28,15 +28,23 @@ struct arch_timer_kvm {
cycle_t cntvoff;
};
-struct arch_timer_cpu {
+struct arch_timer_context {
/* Registers: control register, timer value */
- u32 cntv_ctl; /* Saved/restored */
- cycle_t cntv_cval; /* Saved/restored */
+ u32 cnt_ctl;
+ cycle_t cnt_cval;
+
+ /* Timer IRQ */
+ struct kvm_irq_level irq;
- /*
- * Anything that is not used directly from assembly code goes
- * here.
- */
+ /* Active IRQ state caching */
+ bool active_cleared_last;
+
+ /* Is the timer enabled */
+ bool enabled;
+};
+
+struct arch_timer_cpu {
+ struct arch_timer_context vtimer;
/* Background timer used when the guest is not running */
struct hrtimer timer;
@@ -46,15 +54,6 @@ struct arch_timer_cpu {
/* Background timer active */
bool armed;
-
- /* Timer IRQ */
- struct kvm_irq_level irq;
-
- /* Active IRQ state caching */
- bool active_cleared_last;
-
- /* Is the timer enabled */
- bool enabled;
};
int kvm_timer_hyp_init(void);
@@ -76,4 +75,5 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu);
+#define vcpu_vtimer(v) (&(v)->arch.timer_cpu.vtimer)
#endif
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 27a1f63..30a64df 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -36,7 +36,7 @@
void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
{
- vcpu->arch.timer_cpu.active_cleared_last = false;
+ vcpu_vtimer(vcpu)->active_cleared_last = false;
}
static cycle_t kvm_phys_timer_read(void)
@@ -104,7 +104,7 @@ static u64 kvm_timer_compute_delta(struct kvm_vcpu *vcpu)
{
cycle_t cval, now;
- cval = vcpu->arch.timer_cpu.cntv_cval;
+ cval = vcpu_vtimer(vcpu)->cnt_cval;
now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
if (now < cval) {
@@ -146,21 +146,21 @@ static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt)
static bool kvm_timer_irq_can_fire(struct kvm_vcpu *vcpu)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
- return !(timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
- (timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE);
+ return !(vtimer->cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
+ (vtimer->cnt_ctl & ARCH_TIMER_CTRL_ENABLE);
}
bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
cycle_t cval, now;
if (!kvm_timer_irq_can_fire(vcpu))
return false;
- cval = timer->cntv_cval;
+ cval = vtimer->cnt_cval;
now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
return cval <= now;
@@ -169,17 +169,17 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
{
int ret;
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
BUG_ON(!vgic_initialized(vcpu->kvm));
- timer->active_cleared_last = false;
- timer->irq.level = new_level;
- trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->irq.irq,
- timer->irq.level);
+ vtimer->active_cleared_last = false;
+ vtimer->irq.level = new_level;
+ trace_kvm_timer_update_irq(vcpu->vcpu_id, vtimer->irq.irq,
+ vtimer->irq.level);
ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
- timer->irq.irq,
- timer->irq.level);
+ vtimer->irq.irq,
+ vtimer->irq.level);
WARN_ON(ret);
}
@@ -189,19 +189,19 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
*/
static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
/*
* If userspace modified the timer registers via SET_ONE_REG before
- * the vgic was initialized, we mustn't set the timer->irq.level value
+ * the vgic was initialized, we mustn't set the vtimer->irq.level value
* because the guest would never see the interrupt. Instead wait
* until we call this function from kvm_timer_flush_hwstate.
*/
- if (!vgic_initialized(vcpu->kvm) || !timer->enabled)
+ if (!vgic_initialized(vcpu->kvm) || !vtimer->enabled)
return -ENODEV;
- if (kvm_timer_should_fire(vcpu) != timer->irq.level)
- kvm_timer_update_irq(vcpu, !timer->irq.level);
+ if (kvm_timer_should_fire(vcpu) != vtimer->irq.level)
+ kvm_timer_update_irq(vcpu, !vtimer->irq.level);
return 0;
}
@@ -251,7 +251,7 @@ void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
*/
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
bool phys_active;
int ret;
@@ -275,8 +275,8 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
* to ensure that hardware interrupts from the timer triggers a guest
* exit.
*/
- phys_active = timer->irq.level ||
- kvm_vgic_map_is_active(vcpu, timer->irq.irq);
+ phys_active = vtimer->irq.level ||
+ kvm_vgic_map_is_active(vcpu, vtimer->irq.irq);
/*
* We want to avoid hitting the (re)distributor as much as
@@ -298,7 +298,7 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
* - cached value is "active clear"
* - value to be programmed is "active clear"
*/
- if (timer->active_cleared_last && !phys_active)
+ if (vtimer->active_cleared_last && !phys_active)
return;
ret = irq_set_irqchip_state(host_vtimer_irq,
@@ -306,7 +306,7 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
phys_active);
WARN_ON(ret);
- timer->active_cleared_last = !phys_active;
+ vtimer->active_cleared_last = !phys_active;
}
/**
@@ -332,7 +332,7 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
const struct kvm_irq_level *irq)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
/*
* The vcpu timer irq number cannot be determined in
@@ -340,7 +340,7 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
* kvm_vcpu_set_target(). To handle this, we determine
* vcpu timer irq number when the vcpu is reset.
*/
- timer->irq.irq = irq->irq;
+ vtimer->irq.irq = irq->irq;
/*
* The bits in CNTV_CTL are architecturally reset to UNKNOWN for ARMv8
@@ -348,7 +348,7 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
* resets the timer to be disabled and unmasked and is compliant with
* the ARMv7 architecture.
*/
- timer->cntv_ctl = 0;
+ vtimer->cnt_ctl = 0;
kvm_timer_update_state(vcpu);
return 0;
@@ -370,17 +370,17 @@ static void kvm_timer_init_interrupt(void *info)
int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
switch (regid) {
case KVM_REG_ARM_TIMER_CTL:
- timer->cntv_ctl = value;
+ vtimer->cnt_ctl = value;
break;
case KVM_REG_ARM_TIMER_CNT:
vcpu->kvm->arch.timer.cntvoff = kvm_phys_timer_read() - value;
break;
case KVM_REG_ARM_TIMER_CVAL:
- timer->cntv_cval = value;
+ vtimer->cnt_cval = value;
break;
default:
return -1;
@@ -392,15 +392,15 @@ int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
switch (regid) {
case KVM_REG_ARM_TIMER_CTL:
- return timer->cntv_ctl;
+ return vtimer->cnt_ctl;
case KVM_REG_ARM_TIMER_CNT:
return kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
case KVM_REG_ARM_TIMER_CVAL:
- return timer->cntv_cval;
+ return vtimer->cnt_cval;
}
return (u64)-1;
}
@@ -459,20 +459,21 @@ int kvm_timer_hyp_init(void)
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
timer_disarm(timer);
- kvm_vgic_unmap_phys_irq(vcpu, timer->irq.irq);
+ kvm_vgic_unmap_phys_irq(vcpu, vtimer->irq.irq);
}
int kvm_timer_enable(struct kvm_vcpu *vcpu)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
struct irq_desc *desc;
struct irq_data *data;
int phys_irq;
int ret;
- if (timer->enabled)
+ if (vtimer->enabled)
return 0;
/*
@@ -494,7 +495,7 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
* Tell the VGIC that the virtual interrupt is tied to a
* physical interrupt. We do that once per VCPU.
*/
- ret = kvm_vgic_map_phys_irq(vcpu, timer->irq.irq, phys_irq);
+ ret = kvm_vgic_map_phys_irq(vcpu, vtimer->irq.irq, phys_irq);
if (ret)
return ret;
@@ -508,7 +509,7 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
* the arch timers are enabled.
*/
if (timecounter)
- timer->enabled = 1;
+ vtimer->enabled = 1;
return 0;
}
diff --git a/virt/kvm/arm/hyp/timer-sr.c b/virt/kvm/arm/hyp/timer-sr.c
index 798866a..4bbd36c 100644
--- a/virt/kvm/arm/hyp/timer-sr.c
+++ b/virt/kvm/arm/hyp/timer-sr.c
@@ -24,12 +24,12 @@
/* vcpu is already in the HYP VA space */
void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
u64 val;
- if (timer->enabled) {
- timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
- timer->cntv_cval = read_sysreg_el0(cntv_cval);
+ if (vtimer->enabled) {
+ vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
+ vtimer->cnt_cval = read_sysreg_el0(cntv_cval);
}
/* Disable the virtual timer */
@@ -47,7 +47,7 @@ void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
{
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
u64 val;
/*
@@ -59,10 +59,10 @@ void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
val |= CNTHCTL_EL1PCTEN;
write_sysreg(val, cnthctl_el2);
- if (timer->enabled) {
+ if (vtimer->enabled) {
write_sysreg(kvm->arch.timer.cntvoff, cntvoff_el2);
- write_sysreg_el0(timer->cntv_cval, cntv_cval);
+ write_sysreg_el0(vtimer->cnt_cval, cntv_cval);
isb();
- write_sysreg_el0(timer->cntv_ctl, cntv_ctl);
+ write_sysreg_el0(vtimer->cnt_ctl, cntv_ctl);
}
}
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Jintack Lim <jintack@cs.columbia.edu> |
|---|---|
| Date | 2016-12-26 18:50 +0100 |
| Subject | [RFC 5/8] KVM: arm64: Add the EL1 physical timer access handler |
| Message-ID | <sSHzk-7KX-29@gated-at.bofh.it> |
| In reply to | #1547359 |
KVM traps on the EL1 phys timer accesses from VMs, but it doesn't handle
those traps. This results in terminating VMs. Instead, set a handler for
the EL1 phys timer access, and inject an undefined exception as an
intermediate step.
Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
---
arch/arm64/kvm/sys_regs.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 87e7e66..fd9e747 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -820,6 +820,30 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
CRm((0b1100 | (((n) >> 3) & 0x3))), Op2(((n) & 0x7)), \
access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
+static bool access_cntp_tval(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ kvm_inject_undefined(vcpu);
+ return true;
+}
+
+static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ kvm_inject_undefined(vcpu);
+ return true;
+}
+
+static bool access_cntp_cval(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ kvm_inject_undefined(vcpu);
+ return true;
+}
+
/*
* Architected system registers.
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -1029,6 +1053,16 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
{ Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b011),
NULL, reset_unknown, TPIDRRO_EL0 },
+ /* CNTP_TVAL_EL0 */
+ { Op0(0b11), Op1(0b011), CRn(0b1110), CRm(0b0010), Op2(0b000),
+ access_cntp_tval },
+ /* CNTP_CTL_EL0 */
+ { Op0(0b11), Op1(0b011), CRn(0b1110), CRm(0b0010), Op2(0b001),
+ access_cntp_ctl },
+ /* CNTP_CVAL_EL0 */
+ { Op0(0b11), Op1(0b011), CRn(0b1110), CRm(0b0010), Op2(0b010),
+ access_cntp_cval },
+
/* PMEVCNTRn_EL0 */
PMU_PMEVCNTR_EL0(0),
PMU_PMEVCNTR_EL0(1),
--
1.9.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web