Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp15844089rwd; Mon, 26 Jun 2023 02:02:11 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4EuDWOJj5I2Xop4qORjpbePbu/iP4QAEN4XGNDNmv3YZV1km6ecbjSO/kdVM86B0k04A+G X-Received: by 2002:a17:907:9618:b0:98e:f13:e908 with SMTP id gb24-20020a170907961800b0098e0f13e908mr4401091ejc.60.1687770131087; Mon, 26 Jun 2023 02:02:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687770131; cv=none; d=google.com; s=arc-20160816; b=vGubUZXmSUJmBRNwEs1f1sgPuzVn7owjHTygbDcHg0iByy7ZTb6I8fcWAagttwcGkD MqclkmQ+npglN26B1/2dsIxWbBiEm5ycQ/+/q/cdyfCUK+J2fmsYhcn46Q+HOLkoyIZX 85tzUDtdYqXyoYXV52FYiTlu2J+VZEQSAAnC211Z04fvtGB0D37gbkFanarubpeWmDha 4o8Qr7xn3cg0iNxSZl5kkdUhbUJ+fs+EVSasAQEh7dMszP4j9PJuUGLwZuKCeGbhp6N6 BGBINLQHEOt2qiNNY1NOgvgrVCvzF0zubs9WITdfgBaTB7tr/v4n3s5mUxevAqSVV7U9 huuA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=kFQoVn8HoOGm/hzQ3rlDFPAOWVF5PotnA3/yBNsXzHY=; fh=FJJE1KfQOXNgUCxk/Gxke8tBG9gBLo3aFI+sNzAMBeU=; b=izVscJvLgW0OZhdpOBdISY5ct/rLRxmvb0OXnrrUEtXOuyTDIQLtXy2W3LxhMxwQfR YtxYNY2g0DlFxO7c8AXC/TUhgxUQ0lU1T96ku9ESuRRSuFS9OA2mgsv/Ab/oKL2ixojQ zSRTGOVlPOPCx8TWDMGwQPgNgrvaD3+b/sg+3Rbh50bOEo2+wtO/73m2lldYbqqeccpw EDtLFjPUaSGHPoznO+bMEkWbg0JdrB43DTGNQIuFtyqq0Pl/JbOCeYjD5Zd3dJ3P7MDN DouFrni8WBL9xwy1oGztfu6rlVJ/9JHzagMXLq4gTF0wa7yrDwfRM83URgnQEq7fURwU nXwg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id rv22-20020a17090710d600b009889b68958asi2564371ejb.784.2023.06.26.02.01.39; Mon, 26 Jun 2023 02:02:11 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230514AbjFZIuO (ORCPT + 99 others); Mon, 26 Jun 2023 04:50:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40888 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230509AbjFZIs4 (ORCPT ); Mon, 26 Jun 2023 04:48:56 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 4A70A171F; Mon, 26 Jun 2023 01:48:10 -0700 (PDT) Received: from loongson.cn (unknown [10.2.5.185]) by gateway (Coremail) with SMTP id _____8CxY8TCUJlk8DQCAA--.3315S3; Mon, 26 Jun 2023 16:48:02 +0800 (CST) Received: from localhost.localdomain (unknown [10.2.5.185]) by localhost.localdomain (Coremail) with SMTP id AQAAf8Bxxsy5UJlk4hQJAA--.19269S20; Mon, 26 Jun 2023 16:48:00 +0800 (CST) From: Tianrui Zhao To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: Paolo Bonzini , Huacai Chen , WANG Xuerui , Greg Kroah-Hartman , loongarch@lists.linux.dev, Jens Axboe , Mark Brown , Alex Deucher , Oliver Upton , maobibo@loongson.cn, Xi Ruoyao , zhaotianrui@loongson.cn, tangyouling@loongson.cn Subject: [PATCH v15 18/30] LoongArch: KVM: Implement vcpu timer operations Date: Mon, 26 Jun 2023 16:47:40 +0800 Message-Id: <20230626084752.1138621-19-zhaotianrui@loongson.cn> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230626084752.1138621-1-zhaotianrui@loongson.cn> References: <20230626084752.1138621-1-zhaotianrui@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CM-TRANSID: AQAAf8Bxxsy5UJlk4hQJAA--.19269S20 X-CM-SenderInfo: p2kd03xldq233l6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7 ZEXasCq-sGcSsGvfJ3UbIjqfuFe4nvWSU5nxnvy29KBjDU0xBIdaVrnUUvcSsGvfC2Kfnx nUUI43ZEXa7xR_UUUUUUUUU== X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement LoongArch vcpu timer operations such as init kvm timer, require kvm timer, save kvm timer and restore kvm timer. When vcpu exit, we use kvm soft timer to emulate hardware timer. If timeout happens, the vcpu timer interrupt will be set and it is going to be handled at vcpu next entrance. Reviewed-by: Bibo Mao Signed-off-by: Tianrui Zhao --- arch/loongarch/kvm/timer.c | 266 +++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 arch/loongarch/kvm/timer.c diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c new file mode 100644 index 000000000000..1c81d0549888 --- /dev/null +++ b/arch/loongarch/kvm/timer.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ + +#include +#include +#include + +/* 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_queue_irq(vcpu, INT_TI); + rcuwait_wake_up(&vcpu->wait); + return kvm_count_timeout(vcpu); +} + +/* + * ktime_to_tick() - Scale ktime_t to a 64-bit stable timer. + * + * Caches the dynamic nanosecond bias in vcpu->arch.timer_dyn_bias. + */ +static unsigned long ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now) +{ + s64 now_ns, periods; + unsigned long delta; + + now_ns = ktime_to_ns(now); + delta = now_ns + vcpu->arch.timer_dyn_bias; + + if (delta >= vcpu->arch.timer_period_ns) { + /* If delta is out of safe range the bias needs adjusting */ + periods = div64_s64(now_ns, vcpu->arch.timer_period_ns); + vcpu->arch.timer_dyn_bias = -periods * vcpu->arch.timer_period_ns; + /* Recalculate delta with new bias */ + delta = now_ns + vcpu->arch.timer_dyn_bias; + } + + /* + * We've ensured that: + * delta < timer_period_ns + */ + return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC); +} + +/** + * kvm_resume_hrtimer() - Resume hrtimer, updating expiry. + * @vcpu: Virtual CPU. + * @now: ktime at point of resume. + * @val: stable timer at point of resume. + * + * Resumes the timer and updates the timer expiry based on @now and @count. + */ +static void kvm_resume_hrtimer(struct kvm_vcpu *vcpu, ktime_t now, + unsigned long val) +{ + unsigned long delta; + ktime_t expire; + + /* Stable timer decreased to zero or + * initialize to zero, set 4 second timer + */ + delta = div_u64(val * MNSEC_PER_SEC, vcpu->arch.timer_mhz); + expire = ktime_add_ns(now, delta); + + /* Update hrtimer to use new timeout */ + hrtimer_cancel(&vcpu->arch.swtimer); + hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED); +} + +/** + * kvm_init_timer() - Initialise stable timer. + * @vcpu: Virtual CPU. + * @timer_hz: Frequency of timer. + * + * Initialise the timer to the specified frequency, zero it, and set it going if + * it's enabled. + */ +void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz) +{ + ktime_t now; + unsigned long ticks; + struct loongarch_csrs *csr = vcpu->arch.csr; + + ticks = (unsigned long)MNSEC_PER_SEC * CSR_TCFG_VAL; + vcpu->arch.timer_mhz = timer_hz >> 20; + vcpu->arch.timer_period_ns = div_u64(ticks, vcpu->arch.timer_mhz); + vcpu->arch.timer_dyn_bias = 0; + + /* Starting at 0 */ + ticks = 0; + now = ktime_get(); + vcpu->arch.timer_bias = ticks - ktime_to_tick(vcpu, now); + vcpu->arch.timer_bias &= CSR_TCFG_VAL; + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_TVAL, ticks); +} + +/** + * kvm_count_timeout() - Push timer forward on timeout. + * @vcpu: Virtual CPU. + * + * Handle an hrtimer event by push the hrtimer forward a period. + * + * Returns: The hrtimer_restart value to return to the hrtimer subsystem. + */ +enum hrtimer_restart kvm_count_timeout(struct kvm_vcpu *vcpu) +{ + unsigned long cfg; + + /* Add the Count period to the current expiry time */ + cfg = kvm_read_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TCFG); + if (cfg & CSR_TCFG_PERIOD) { + hrtimer_add_expires_ns(&vcpu->arch.swtimer, cfg & CSR_TCFG_VAL); + return HRTIMER_RESTART; + } else + return HRTIMER_NORESTART; +} + +/* + * kvm_restore_timer() - Restore timer state. + * @vcpu: Virtual CPU. + * + * Restore soft timer state from saved context. + */ +void kvm_restore_timer(struct kvm_vcpu *vcpu) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + ktime_t saved_ktime, now; + unsigned long val, new, delta; + int expired = 0; + unsigned long cfg; + + /* + * Set guest stable timer cfg csr + */ + cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG); + kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); + if (!(cfg & CSR_TCFG_EN)) { + kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG); + kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL); + return; + } + + now = ktime_get(); + saved_ktime = vcpu->arch.stable_ktime_saved; + val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL); + + /*hrtimer not expire */ + delta = ktime_to_tick(vcpu, ktime_sub(now, saved_ktime)); + if (delta >= val) { + expired = 1; + if (cfg & CSR_TCFG_PERIOD) + new = (delta - val) % (cfg & CSR_TCFG_VAL); + else + new = 1; + } else + new = val - delta; + + new &= CSR_TCFG_VAL; + write_gcsr_timercfg(cfg); + write_gcsr_timertick(new); + if (expired) + _kvm_queue_irq(vcpu, INT_TI); +} + +/* + * kvm_acquire_timer() - Switch to hard timer state. + * @vcpu: Virtual CPU. + * + * Restore hard timer state on top of existing soft timer state if possible. + * + * Since hard timer won't remain active over preemption, preemption should be + * disabled by the caller. + */ +void kvm_acquire_timer(struct kvm_vcpu *vcpu) +{ + unsigned long flags, guestcfg; + + guestcfg = read_csr_gcfg(); + if (!(guestcfg & CSR_GCFG_TIT)) + return; + + /* enable guest access to hard timer */ + write_csr_gcfg(guestcfg & ~CSR_GCFG_TIT); + + /* + * Freeze the soft-timer and sync the guest stable timer with it. We do + * this with interrupts disabled to avoid latency. + */ + local_irq_save(flags); + hrtimer_cancel(&vcpu->arch.swtimer); + local_irq_restore(flags); +} + + +/* + * _kvm_save_timer() - Switch to software emulation of guest timer. + * @vcpu: Virtual CPU. + * + * 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 ktime_t _kvm_save_timer(struct kvm_vcpu *vcpu, unsigned long *val) +{ + unsigned long end_time; + ktime_t before_time; + + before_time = ktime_get(); + + /* + * Record a final stable timer which we will transfer to the soft-timer. + */ + end_time = read_gcsr_timertick(); + *val = end_time; + + kvm_resume_hrtimer(vcpu, before_time, end_time); + return before_time; +} + +/* + * kvm_save_timer() - Save guest timer state. + * @vcpu: Virtual CPU. + * + * 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; + unsigned long guestcfg, val; + ktime_t save_ktime; + + preempt_disable(); + guestcfg = read_csr_gcfg(); + if (!(guestcfg & CSR_GCFG_TIT)) { + /* disable guest use of hard timer */ + write_csr_gcfg(guestcfg | CSR_GCFG_TIT); + + /* save hard timer state */ + kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG); + if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN) { + save_ktime = _kvm_save_timer(vcpu, &val); + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_TVAL, val); + vcpu->arch.stable_ktime_saved = save_ktime; + if (val == CSR_TCFG_VAL) + _kvm_queue_irq(vcpu, INT_TI); + } else { + kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL); + } + } + + /* save timer-related state to vCPU context */ + kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); + preempt_enable(); +} + +void kvm_reset_timer(struct kvm_vcpu *vcpu) +{ + write_gcsr_timercfg(0); + kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TCFG, 0); + hrtimer_cancel(&vcpu->arch.swtimer); +} -- 2.39.1