Skip to content

Commit 338f8ea

Browse files
author
Marc Zyngier
committed
KVM: arm64: nv: Accelerate EL0 timer read accesses when FEAT_ECV in use
Although FEAT_ECV allows us to correctly emulate the timers, it also reduces performances pretty badly. Mitigate this by emulating the CTL/CVAL register reads in the inner run loop, without returning to the general kernel. Acked-by: Oliver Upton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent 2cd2a77 commit 338f8ea

File tree

4 files changed

+122
-18
lines changed

4 files changed

+122
-18
lines changed

arch/arm64/kvm/arch_timer.c

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,6 @@ u64 timer_get_cval(struct arch_timer_context *ctxt)
101101
}
102102
}
103103

104-
static u64 timer_get_offset(struct arch_timer_context *ctxt)
105-
{
106-
u64 offset = 0;
107-
108-
if (!ctxt)
109-
return 0;
110-
111-
if (ctxt->offset.vm_offset)
112-
offset += *ctxt->offset.vm_offset;
113-
if (ctxt->offset.vcpu_offset)
114-
offset += *ctxt->offset.vcpu_offset;
115-
116-
return offset;
117-
}
118-
119104
static void timer_set_ctl(struct arch_timer_context *ctxt, u32 ctl)
120105
{
121106
struct kvm_vcpu *vcpu = ctxt->vcpu;
@@ -964,10 +949,10 @@ void kvm_timer_sync_nested(struct kvm_vcpu *vcpu)
964949
* which allows trapping of the timer registers even with NV2.
965950
* Still, this is still worse than FEAT_NV on its own. Meh.
966951
*/
967-
if (cpus_have_final_cap(ARM64_HAS_ECV) || !is_hyp_ctxt(vcpu))
968-
return;
969-
970952
if (!vcpu_el2_e2h_is_set(vcpu)) {
953+
if (cpus_have_final_cap(ARM64_HAS_ECV))
954+
return;
955+
971956
/*
972957
* A non-VHE guest hypervisor doesn't have any direct access
973958
* to its timers: the EL2 registers trap (and the HW is

arch/arm64/kvm/hyp/include/hyp/switch.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,11 @@ static inline bool handle_tx2_tvm(struct kvm_vcpu *vcpu)
501501
return true;
502502
}
503503

504+
static inline u64 compute_counter_value(struct arch_timer_context *ctxt)
505+
{
506+
return arch_timer_read_cntpct_el0() - timer_get_offset(ctxt);
507+
}
508+
504509
static bool kvm_hyp_handle_cntpct(struct kvm_vcpu *vcpu)
505510
{
506511
struct arch_timer_context *ctxt;

arch/arm64/kvm/hyp/vhe/switch.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,102 @@ void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
256256
host_data_ptr(host_ctxt)->__hyp_running_vcpu = NULL;
257257
}
258258

259+
static u64 compute_emulated_cntx_ctl_el0(struct kvm_vcpu *vcpu,
260+
enum vcpu_sysreg reg)
261+
{
262+
unsigned long ctl;
263+
u64 cval, cnt;
264+
bool stat;
265+
266+
switch (reg) {
267+
case CNTP_CTL_EL0:
268+
cval = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
269+
ctl = __vcpu_sys_reg(vcpu, CNTP_CTL_EL0);
270+
cnt = compute_counter_value(vcpu_ptimer(vcpu));
271+
break;
272+
case CNTV_CTL_EL0:
273+
cval = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
274+
ctl = __vcpu_sys_reg(vcpu, CNTV_CTL_EL0);
275+
cnt = compute_counter_value(vcpu_vtimer(vcpu));
276+
break;
277+
default:
278+
BUG();
279+
}
280+
281+
stat = cval <= cnt;
282+
__assign_bit(__ffs(ARCH_TIMER_CTRL_IT_STAT), &ctl, stat);
283+
284+
return ctl;
285+
}
286+
287+
static bool kvm_hyp_handle_timer(struct kvm_vcpu *vcpu, u64 *exit_code)
288+
{
289+
u64 esr, val;
290+
291+
/*
292+
* Having FEAT_ECV allows for a better quality of timer emulation.
293+
* However, this comes at a huge cost in terms of traps. Try and
294+
* satisfy the reads from guest's hypervisor context without
295+
* returning to the kernel if we can.
296+
*/
297+
if (!is_hyp_ctxt(vcpu))
298+
return false;
299+
300+
esr = kvm_vcpu_get_esr(vcpu);
301+
if ((esr & ESR_ELx_SYS64_ISS_DIR_MASK) != ESR_ELx_SYS64_ISS_DIR_READ)
302+
return false;
303+
304+
switch (esr_sys64_to_sysreg(esr)) {
305+
case SYS_CNTP_CTL_EL02:
306+
val = compute_emulated_cntx_ctl_el0(vcpu, CNTP_CTL_EL0);
307+
break;
308+
case SYS_CNTP_CTL_EL0:
309+
if (vcpu_el2_e2h_is_set(vcpu))
310+
val = read_sysreg_el0(SYS_CNTP_CTL);
311+
else
312+
val = compute_emulated_cntx_ctl_el0(vcpu, CNTP_CTL_EL0);
313+
break;
314+
case SYS_CNTP_CVAL_EL02:
315+
val = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
316+
break;
317+
case SYS_CNTP_CVAL_EL0:
318+
if (vcpu_el2_e2h_is_set(vcpu)) {
319+
val = read_sysreg_el0(SYS_CNTP_CVAL);
320+
321+
if (!has_cntpoff())
322+
val -= timer_get_offset(vcpu_hptimer(vcpu));
323+
} else {
324+
val = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
325+
}
326+
break;
327+
case SYS_CNTV_CTL_EL02:
328+
val = compute_emulated_cntx_ctl_el0(vcpu, CNTV_CTL_EL0);
329+
break;
330+
case SYS_CNTV_CTL_EL0:
331+
if (vcpu_el2_e2h_is_set(vcpu))
332+
val = read_sysreg_el0(SYS_CNTV_CTL);
333+
else
334+
val = compute_emulated_cntx_ctl_el0(vcpu, CNTV_CTL_EL0);
335+
break;
336+
case SYS_CNTV_CVAL_EL02:
337+
val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
338+
break;
339+
case SYS_CNTV_CVAL_EL0:
340+
if (vcpu_el2_e2h_is_set(vcpu))
341+
val = read_sysreg_el0(SYS_CNTV_CVAL);
342+
else
343+
val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
344+
break;
345+
default:
346+
return false;
347+
}
348+
349+
vcpu_set_reg(vcpu, kvm_vcpu_sys_get_rt(vcpu), val);
350+
__kvm_skip_instr(vcpu);
351+
352+
return true;
353+
}
354+
259355
static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code)
260356
{
261357
u64 esr = kvm_vcpu_get_esr(vcpu);
@@ -409,6 +505,9 @@ static bool kvm_hyp_handle_sysreg_vhe(struct kvm_vcpu *vcpu, u64 *exit_code)
409505
if (kvm_hyp_handle_tlbi_el2(vcpu, exit_code))
410506
return true;
411507

508+
if (kvm_hyp_handle_timer(vcpu, exit_code))
509+
return true;
510+
412511
if (kvm_hyp_handle_cpacr_el1(vcpu, exit_code))
413512
return true;
414513

include/kvm/arm_arch_timer.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,19 @@ static inline bool has_cntpoff(void)
156156
return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));
157157
}
158158

159+
static inline u64 timer_get_offset(struct arch_timer_context *ctxt)
160+
{
161+
u64 offset = 0;
162+
163+
if (!ctxt)
164+
return 0;
165+
166+
if (ctxt->offset.vm_offset)
167+
offset += *ctxt->offset.vm_offset;
168+
if (ctxt->offset.vcpu_offset)
169+
offset += *ctxt->offset.vcpu_offset;
170+
171+
return offset;
172+
}
173+
159174
#endif

0 commit comments

Comments
 (0)