Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
#include <linux/kvm_host.h>#include <asm/delay.h>#include <asm/kvm_csr.h>#include <asm/kvm_vcpu.h>
/* * ktime_to_tick() - Scale ktime_t to timer tick value. */static inline u64 ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now){ u64 delta;
delta = ktime_to_ns(now); return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC);}
static inline u64 tick_to_ns(struct kvm_vcpu *vcpu, u64 tick){ return div_u64(tick * MNSEC_PER_SEC, vcpu->arch.timer_mhz);}
/* Low level hrtimer wake routine */enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer){ struct kvm_vcpu *vcpu;
vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); kvm_vcpu_wake_up(vcpu);
return HRTIMER_NORESTART;}
/* * Initialise the timer to the specified frequency, zero it */void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz){ vcpu->arch.timer_mhz = timer_hz >> 20;
/* Starting at 0 */ kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TVAL, 0);}
/* * Restore soft timer state from saved context. */void kvm_restore_timer(struct kvm_vcpu *vcpu){ unsigned long cfg, estat; unsigned long ticks, delta, period; ktime_t expire, now; struct loongarch_csrs *csr = vcpu->arch.csr;
/* * Set guest stable timer cfg csr * Disable timer before restore estat CSR register, avoid to * get invalid timer interrupt for old timer cfg */ cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);
write_gcsr_timercfg(0); kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG); if (!(cfg & CSR_TCFG_EN)) { /* Guest timer is disabled, just restore timer registers */ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL); return; }
/* * Freeze the soft-timer and sync the guest stable timer with it. */ if (kvm_vcpu_is_blocking(vcpu)) hrtimer_cancel(&vcpu->arch.swtimer);
/* * From LoongArch Reference Manual Volume 1 Chapter 7.6.2 * If oneshot timer is fired, CSR TVAL will be -1, there are two * conditions: * 1) timer is fired during exiting to host * 2) timer is fired and vm is doing timer irq, and then exiting to * host. Host should not inject timer irq to avoid spurious * timer interrupt again */ ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL); estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT); if (!(cfg & CSR_TCFG_PERIOD) && (ticks > cfg)) { /* * Writing 0 to LOONGARCH_CSR_TVAL will inject timer irq * and set CSR TVAL with -1 */ write_gcsr_timertick(0);
/* * Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear * timer interrupt, and CSR TVAL keeps unchanged with -1, it * avoids spurious timer interrupt */ if (!(estat & CPU_TIMER)) { __delay(2); /* Wait cycles until timer interrupt injected */
/* Write TVAL with max value if no TI shot */ estat = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT); if (!(estat & CPU_TIMER)) write_gcsr_timertick(CSR_TCFG_VAL); gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); } return; }
/* * Set remainder tick value if not expired */ delta = 0; now = ktime_get(); expire = vcpu->arch.expire; if (!expire) { /* * vcpu->arch.expire is host-internal and is not migrated, * so it is 0 after migration. Reload the remaining countdown * from the migrated TVAL. This covers both one-shot and * periodic timers. */ if (ticks < cfg) delta = tick_to_ns(vcpu, ticks); expire = ktime_add_ns(now, delta); }
if (ktime_before(now, expire)) delta = ktime_to_tick(vcpu, ktime_sub(expire, now)); else if (cfg & CSR_TCFG_PERIOD) { period = cfg & CSR_TCFG_VAL; if (!period) period = 1; delta = ktime_to_tick(vcpu, ktime_sub(now, expire)); delta = period - (delta % period);
/* * Inject timer here though sw timer should inject timer * interrupt async already, since sw timer may be cancelled * during injecting intr async */ kvm_queue_irq(vcpu, INT_TI); }
write_gcsr_timertick(delta);}
/* * Save guest timer state and switch to software emulation of guest * timer. The hard timer must already be in use, so preemption should be * disabled. */static void _kvm_save_timer(struct kvm_vcpu *vcpu){ unsigned long ticks, delta, cfg; ktime_t expire; struct loongarch_csrs *csr = vcpu->arch.csr;
cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG); ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);
/* * From LoongArch Reference Manual Volume 1 Chapter 7.6.2 * If period timer is fired, CSR TVAL will be reloaded from CSR TCFG * If oneshot timer is fired, CSR TVAL will be -1 * Here judge one-shot timer fired by checking whether TVAL is larger * than TCFG */ if (ticks < cfg) delta = tick_to_ns(vcpu, ticks); else delta = 0;
expire = ktime_add_ns(ktime_get(), delta); vcpu->arch.expire = expire; if (kvm_vcpu_is_blocking(vcpu)) {
/* * HRTIMER_MODE_PINNED_HARD is suggested since vcpu may run in * the same physical cpu in next time, and the timer should run * in hardirq context even in the PREEMPT_RT case. */ hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED_HARD); }}
/* * Save guest timer state and switch to soft guest timer if hard timer was in * use. */void kvm_save_timer(struct kvm_vcpu *vcpu){ struct loongarch_csrs *csr = vcpu->arch.csr;
preempt_disable();
/* Save hard timer state */ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG); kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL); if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN) _kvm_save_timer(vcpu);
/* Save timer-related state to vCPU context */ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); preempt_enable();}