From patchwork Mon Jul 19 18:49:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386797 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 36193C6379C for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 50715611ED for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387871AbhGSUgG (ORCPT ); Mon, 19 Jul 2021 16:36:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58808 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383737AbhGSSJZ (ORCPT ); Mon, 19 Jul 2021 14:09:25 -0400 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0EF31C0613E2 for ; Mon, 19 Jul 2021 11:38:08 -0700 (PDT) Received: by mail-qk1-x74a.google.com with SMTP id y5-20020a37af050000b02903a9c3f8b89fso15547454qke.2 for ; Mon, 19 Jul 2021 11:50:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=WmFQqCYG7AcxWe1pr4fwUtigJmQCR7e0nSEWHDj1Hes=; b=MM5/EW3dU5nhpT4cIj0DDuGahi7u3lEd+VTdJIHIX7d3U7xpLaZripodUBPiOmZ9cR kjTlF+O2Wyq5Asougl8gByz6BOiXfKCJ5FcbsYSlLt9Vm8b10ePwmQEJ3yILluF6avwD 3ePi3BzEJ6FmBj834gyWQSx0zUgIPnHORPMwAouP2HZZ2uU0LS0I3xfhsK3P8Z8FZt17 iugA6xLW1VQs5JamNUUKo/eKFxIdbombt+/uONIiX33f4zbg86wmHU7ti5OuEMB7UOdN cXUrIvkzdhRrNRwnf3kO0llmueMKy/+9nMV1/XE+cGrHLIfgG0huQBswW1Ul00GtCdqD 1jhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=WmFQqCYG7AcxWe1pr4fwUtigJmQCR7e0nSEWHDj1Hes=; b=Pjk4sFOj4Y9w0p+UW2rBbjDhwz3LIR/XGKm78MjLxYTnvms0e/iexZl9Te0hbS2Ckc jJRGxkJ6k3KG//nh1G+5BvSjSmVAmxAf3rCjPbr11dMqj1K9P5P+sywsAZTi8mz40DXn s+O140IqFpWTUpNjt7YXiqj14kuMVRbi5XoPDu72uCHVrQtqRGpNWB1cjOixGUqVV9J5 Dmvec00idYX02i6QpylKiJowffm+mX60sWjjxIbyo1jmVBMLMqrtl1kUauAkWXz54OpD QbFqDjgig34sdUuJoxOKoKGAEdMYkRHux6XMCXkNWbnYXmNwxKy0amf+a4yrQJnYYLxg UcYA== X-Gm-Message-State: AOAM5312PTz3JhqNRL7kPsspFxnY05SOAK9ld9HNrpgeYPymNpYm1d7h PMOLgXY7KfZ239oPiKvM6PeHpW+2r6SVAK/gaXCqWiYKJuXIhwX4MiiYCwYLv5dsH+ldc2hIGPS vBR56abZ3SPa3a06P6jgbPK/KE95LplqaSS70trieqJaRJaF6BUfrEUTx9w== X-Google-Smtp-Source: ABdhPJxB9rOK0wVybvbKYBTAVFVh+t+nxs2BMJ/KRhqyzfiS5kPiWMjelwIQUfWQ2qz/3xteHhk3bF+H8nE= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:ad4:4d8f:: with SMTP id cv15mr26507188qvb.9.1626720601324; Mon, 19 Jul 2021 11:50:01 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:38 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-2-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 01/12] KVM: x86: Report host tsc and realtime values in KVM_GET_CLOCK From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Handling the migration of TSCs correctly is difficult, in part because Linux does not provide userspace with the ability to retrieve a (TSC, realtime) clock pair for a single instant in time. In lieu of a more convenient facility, KVM can report similar information in the kvm_clock structure. Provide userspace with a host TSC & realtime pair iff the realtime clock is based on the TSC. If userspace provides KVM_SET_CLOCK with a valid realtime value, advance the KVM clock by the amount of elapsed time. Do not step the KVM clock backwards, though, as it is a monotonic oscillator. Signed-off-by: Oliver Upton --- Documentation/virt/kvm/api.rst | 42 +++++++-- arch/x86/include/asm/kvm_host.h | 3 + arch/x86/kvm/x86.c | 149 ++++++++++++++++++++------------ include/uapi/linux/kvm.h | 7 +- 4 files changed, 137 insertions(+), 64 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index b9ddce5638f5..70ccf68cd574 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -993,20 +993,34 @@ such as migration. When KVM_CAP_ADJUST_CLOCK is passed to KVM_CHECK_EXTENSION, it returns the set of bits that KVM can return in struct kvm_clock_data's flag member. -The only flag defined now is KVM_CLOCK_TSC_STABLE. If set, the returned -value is the exact kvmclock value seen by all VCPUs at the instant -when KVM_GET_CLOCK was called. If clear, the returned value is simply -CLOCK_MONOTONIC plus a constant offset; the offset can be modified -with KVM_SET_CLOCK. KVM will try to make all VCPUs follow this clock, -but the exact value read by each VCPU could differ, because the host -TSC is not stable. +FLAGS: + +KVM_CLOCK_TSC_STABLE. If set, the returned value is the exact kvmclock +value seen by all VCPUs at the instant when KVM_GET_CLOCK was called. +If clear, the returned value is simply CLOCK_MONOTONIC plus a constant +offset; the offset can be modified with KVM_SET_CLOCK. KVM will try +to make all VCPUs follow this clock, but the exact value read by each +VCPU could differ, because the host TSC is not stable. + +KVM_CLOCK_REALTIME. If set, the `realtime` field in the kvm_clock_data +structure is populated with the value of the host's real time +clocksource at the instant when KVM_GET_CLOCK was called. If clear, +the `realtime` field does not contain a value. + +KVM_CLOCK_HOST_TSC. If set, the `host_tsc` field in the kvm_clock_data +structure is populated with the value of the host's timestamp counter (TSC) +at the instant when KVM_GET_CLOCK was called. If clear, the `host_tsc` field +does not contain a value. :: struct kvm_clock_data { __u64 clock; /* kvmclock current value */ __u32 flags; - __u32 pad[9]; + __u32 pad0; + __u64 realtime; + __u64 host_tsc; + __u32 pad[4]; }; @@ -1023,12 +1037,22 @@ Sets the current timestamp of kvmclock to the value specified in its parameter. In conjunction with KVM_GET_CLOCK, it is used to ensure monotonicity on scenarios such as migration. +FLAGS: + +KVM_CLOCK_REALTIME. If set, KVM will compare the value of the `realtime` field +with the value of the host's real time clocksource at the instant when +KVM_SET_CLOCK was called. The difference in elapsed time is added to the final +kvmclock value that will be provided to guests. + :: struct kvm_clock_data { __u64 clock; /* kvmclock current value */ __u32 flags; - __u32 pad[9]; + __u32 pad0; + __u64 realtime; + __u64 host_tsc; + __u32 pad[4]; }; diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 974cbfb1eefe..3fb2b9270d01 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1936,4 +1936,7 @@ int kvm_cpu_dirty_log_size(void); int alloc_all_memslots_rmaps(struct kvm *kvm); +#define KVM_CLOCK_VALID_FLAGS \ + (KVM_CLOCK_TSC_STABLE | KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC) + #endif /* _ASM_X86_KVM_HOST_H */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c471b1375f36..bff78168d4a2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2780,17 +2780,24 @@ static void kvm_gen_update_masterclock(struct kvm *kvm) #endif } -u64 get_kvmclock_ns(struct kvm *kvm) +/* + * Returns true if realtime and TSC values were written back to the caller. + * Returns false if a clock triplet cannot be obtained, such as if the host's + * realtime clock is not based on the TSC. + */ +static bool get_kvmclock_and_realtime(struct kvm *kvm, u64 *kvmclock_ns, + u64 *realtime_ns, u64 *tsc) { struct kvm_arch *ka = &kvm->arch; struct pvclock_vcpu_time_info hv_clock; unsigned long flags; - u64 ret; + bool ret = false; spin_lock_irqsave(&ka->pvclock_gtod_sync_lock, flags); if (!ka->use_master_clock) { spin_unlock_irqrestore(&ka->pvclock_gtod_sync_lock, flags); - return get_kvmclock_base_ns() + ka->kvmclock_offset; + *kvmclock_ns = get_kvmclock_base_ns() + ka->kvmclock_offset; + return false; } hv_clock.tsc_timestamp = ka->master_cycle_now; @@ -2801,18 +2808,36 @@ u64 get_kvmclock_ns(struct kvm *kvm) get_cpu(); if (__this_cpu_read(cpu_tsc_khz)) { + struct timespec64 ts; + u64 tsc_val; + kvm_get_time_scale(NSEC_PER_SEC, __this_cpu_read(cpu_tsc_khz) * 1000LL, &hv_clock.tsc_shift, &hv_clock.tsc_to_system_mul); - ret = __pvclock_read_cycles(&hv_clock, rdtsc()); + + if (kvm_get_walltime_and_clockread(&ts, &tsc_val)) { + *realtime_ns = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec; + *tsc = tsc_val; + ret = true; + } + + *kvmclock_ns = __pvclock_read_cycles(&hv_clock, tsc_val); } else - ret = get_kvmclock_base_ns() + ka->kvmclock_offset; + *kvmclock_ns = get_kvmclock_base_ns() + ka->kvmclock_offset; put_cpu(); return ret; } +u64 get_kvmclock_ns(struct kvm *kvm) +{ + u64 kvmclock_ns, realtime_ns, tsc; + + get_kvmclock_and_realtime(kvm, &kvmclock_ns, &realtime_ns, &tsc); + return kvmclock_ns; +} + static void kvm_setup_pvclock_page(struct kvm_vcpu *v, struct gfn_to_hva_cache *cache, unsigned int offset) @@ -4031,7 +4056,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = KVM_SYNC_X86_VALID_FIELDS; break; case KVM_CAP_ADJUST_CLOCK: - r = KVM_CLOCK_TSC_STABLE; + r = KVM_CLOCK_VALID_FLAGS; break; case KVM_CAP_X86_DISABLE_EXITS: r |= KVM_X86_DISABLE_EXITS_HLT | KVM_X86_DISABLE_EXITS_PAUSE | @@ -5806,6 +5831,68 @@ int kvm_arch_pm_notifier(struct kvm *kvm, unsigned long state) } #endif /* CONFIG_HAVE_KVM_PM_NOTIFIER */ +static int kvm_vm_ioctl_get_clock(struct kvm *kvm, + void __user *argp) +{ + struct kvm_clock_data data; + + memset(&data, 0, sizeof(data)); + + if (get_kvmclock_and_realtime(kvm, &data.clock, &data.realtime, + &data.host_tsc)) + data.flags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC; + + if (kvm->arch.use_master_clock) + data.flags |= KVM_CLOCK_TSC_STABLE; + + if (copy_to_user(argp, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +static int kvm_vm_ioctl_set_clock(struct kvm *kvm, + void __user *argp) +{ + struct kvm_arch *ka = &kvm->arch; + struct kvm_clock_data data; + u64 now_raw_ns; + + if (copy_from_user(&data, argp, sizeof(data))) + return -EFAULT; + + if (data.flags & ~KVM_CLOCK_REALTIME) + return -EINVAL; + + /* + * TODO: userspace has to take care of races with VCPU_RUN, so + * kvm_gen_update_masterclock() can be cut down to locked + * pvclock_update_vm_gtod_copy(). + */ + kvm_gen_update_masterclock(kvm); + + spin_lock_irq(&ka->pvclock_gtod_sync_lock); + if (data.flags & KVM_CLOCK_REALTIME) { + u64 now_real_ns = ktime_get_real_ns(); + + /* + * Avoid stepping the kvmclock backwards. + */ + if (now_real_ns > data.realtime) + data.clock += now_real_ns - data.realtime; + } + + if (ka->use_master_clock) + now_raw_ns = ka->master_kernel_ns; + else + now_raw_ns = get_kvmclock_base_ns(); + ka->kvmclock_offset = data.clock - now_raw_ns; + spin_unlock_irq(&ka->pvclock_gtod_sync_lock); + + kvm_make_all_cpus_request(kvm, KVM_REQ_CLOCK_UPDATE); + return 0; +} + long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -6050,57 +6137,11 @@ long kvm_arch_vm_ioctl(struct file *filp, } #endif case KVM_SET_CLOCK: { - struct kvm_arch *ka = &kvm->arch; - struct kvm_clock_data user_ns; - u64 now_ns; - - r = -EFAULT; - if (copy_from_user(&user_ns, argp, sizeof(user_ns))) - goto out; - - r = -EINVAL; - if (user_ns.flags) - goto out; - - r = 0; - /* - * TODO: userspace has to take care of races with VCPU_RUN, so - * kvm_gen_update_masterclock() can be cut down to locked - * pvclock_update_vm_gtod_copy(). - */ - kvm_gen_update_masterclock(kvm); - - /* - * This pairs with kvm_guest_time_update(): when masterclock is - * in use, we use master_kernel_ns + kvmclock_offset to set - * unsigned 'system_time' so if we use get_kvmclock_ns() (which - * is slightly ahead) here we risk going negative on unsigned - * 'system_time' when 'user_ns.clock' is very small. - */ - spin_lock_irq(&ka->pvclock_gtod_sync_lock); - if (kvm->arch.use_master_clock) - now_ns = ka->master_kernel_ns; - else - now_ns = get_kvmclock_base_ns(); - ka->kvmclock_offset = user_ns.clock - now_ns; - spin_unlock_irq(&ka->pvclock_gtod_sync_lock); - - kvm_make_all_cpus_request(kvm, KVM_REQ_CLOCK_UPDATE); + r = kvm_vm_ioctl_set_clock(kvm, argp); break; } case KVM_GET_CLOCK: { - struct kvm_clock_data user_ns; - u64 now_ns; - - now_ns = get_kvmclock_ns(kvm); - user_ns.clock = now_ns; - user_ns.flags = kvm->arch.use_master_clock ? KVM_CLOCK_TSC_STABLE : 0; - memset(&user_ns.pad, 0, sizeof(user_ns.pad)); - - r = -EFAULT; - if (copy_to_user(argp, &user_ns, sizeof(user_ns))) - goto out; - r = 0; + r = kvm_vm_ioctl_get_clock(kvm, argp); break; } case KVM_MEMORY_ENCRYPT_OP: { diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index d9e4aabcb31a..53a49cb8616a 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1223,11 +1223,16 @@ struct kvm_irqfd { /* Do not use 1, KVM_CHECK_EXTENSION returned it before we had flags. */ #define KVM_CLOCK_TSC_STABLE 2 +#define KVM_CLOCK_REALTIME (1 << 2) +#define KVM_CLOCK_HOST_TSC (1 << 3) struct kvm_clock_data { __u64 clock; __u32 flags; - __u32 pad[9]; + __u32 pad0; + __u64 realtime; + __u64 host_tsc; + __u32 pad[4]; }; /* For KVM_CAP_SW_TLB */ From patchwork Mon Jul 19 18:49:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386791 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20718C63798 for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 39CEA611C1 for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387862AbhGSUfp (ORCPT ); Mon, 19 Jul 2021 16:35:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58810 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383738AbhGSSJZ (ORCPT ); Mon, 19 Jul 2021 14:09:25 -0400 Received: from mail-io1-xd4a.google.com (mail-io1-xd4a.google.com [IPv6:2607:f8b0:4864:20::d4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F0E6C0613E3 for ; Mon, 19 Jul 2021 11:38:08 -0700 (PDT) Received: by mail-io1-xd4a.google.com with SMTP id b20-20020a0566023314b0290523c137a6a4so13326173ioz.8 for ; Mon, 19 Jul 2021 11:50:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=GhOMvXrghMgrA5cJUDKp3N5lyADxOv1r/pHquF+79AY=; b=D/aYlVxo4gF+ngT2EhsxuBs4dLxMpsc59UY0ea+aZF7kV6yb0h6VJuouAJFhYN/W2V q67XWG04FbHwgm5fOnbL/VXz5mp4tx5GDfjliini9B8ijChsqmUhjZ+plrU/zmqfrx+3 Au91AOJPy2VNvDdARpgsaAt7/h2+A1wxSiqW8VNqqvSmb5Cx6kbqFFiD8m64G7G+mRq5 MZTaYqlzfxaS5qHKar7AkJnWiUZlVkVpnxfDOXBlmlYJBOcs5V2jE4ar1G1IsW/P/ehB zUn5sPy9uUJ1PCJcvTqOLbh9W52HkCfHG42kw4oiidiOsn4m4fz431A7RoIX8b4UZePK Tn/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=GhOMvXrghMgrA5cJUDKp3N5lyADxOv1r/pHquF+79AY=; b=eIHeNQYFQ0Ae8nIkSuaD8Fyw8KKzRH9192sz0TtAxUg2DBdgxTV36XA8lzr9tJTc98 TUcaEmwJQG+FnK9E8FBc8yjIRkpEduWL/SEVAlTl6qi96uBkCupaMF9Pa9pjFrnpx/wV x2jytRipX8rw1AVPKGR8gPJBlMrrLeGYk95PkUqIsoV1GJV2QQYAp0+Yf7UxWYSRlNOU m6pYAI8i8+MQSUlwpC7rc0z0ny6Cs1IlSyHhJlYAC96sVWtHmpoZw81hhzRMd602jfBg METo15CIF9dkbrkXAxFdFcBtLurCRXhHJA0iSzqBwdJXn8kPNXtItPuDFsFbZBMHw5OF ptOA== X-Gm-Message-State: AOAM532CdVqb3YjspryI/mF6/Bjfe10cxZ877TeKJ5ng2Hns5o85Oap9 ZO0w8S3TmB3+ZchJtM7mKs01MwPdcgbMXmNw0Ed/WZfv6redCy/y08g3stcSRzbrgklAwcOviHt K32YE0vTM+2invP+rqo4A2ndN9kTlnbvcWraO0/s9qpaY8zBAEs2O+rGjCg== X-Google-Smtp-Source: ABdhPJxhjeeV517I8G9SDrwYXwQAF4gak5x9fd2mxwZ6Dy0ItSFxpEA3vLF5SwT8/R+AMo1D7Kj5J29roqY= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a92:5409:: with SMTP id i9mr17763897ilb.138.1626720602384; Mon, 19 Jul 2021 11:50:02 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:39 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-3-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 02/12] KVM: x86: Refactor tsc synchronization code From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Refactor kvm_synchronize_tsc to make a new function that allows callers to specify TSC parameters (offset, value, nanoseconds, etc.) explicitly for the sake of participating in TSC synchronization. This changes the locking semantics around TSC writes. Writes to the TSC will now take the pvclock gtod lock while holding the tsc write lock, whereas before these locks were disjoint. Reviewed-by: David Matlack Signed-off-by: Oliver Upton --- Documentation/virt/kvm/locking.rst | 11 +++ arch/x86/kvm/x86.c | 106 +++++++++++++++++------------ 2 files changed, 74 insertions(+), 43 deletions(-) diff --git a/Documentation/virt/kvm/locking.rst b/Documentation/virt/kvm/locking.rst index 35eca377543d..ac62e1c76694 100644 --- a/Documentation/virt/kvm/locking.rst +++ b/Documentation/virt/kvm/locking.rst @@ -30,6 +30,9 @@ On x86: holding kvm->arch.mmu_lock (typically with ``read_lock``, otherwise there's no need to take kvm->arch.tdp_mmu_pages_lock at all). +- kvm->arch.tsc_write_lock is taken outside + kvm->arch.pvclock_gtod_sync_lock + Everything else is a leaf: no other lock is taken inside the critical sections. @@ -216,6 +219,14 @@ time it will be set using the Dirty tracking mechanism described above. :Comment: 'raw' because hardware enabling/disabling must be atomic /wrt migration. +:Name: kvm_arch::pvclock_gtod_sync_lock +:Type: raw_spinlock_t +:Arch: x86 +:Protects: kvm_arch::{cur_tsc_generation,cur_tsc_nsec,cur_tsc_write, + cur_tsc_offset,nr_vcpus_matched_tsc} +:Comment: 'raw' because updating the kvm master clock must not be + preempted. + :Name: kvm_arch::tsc_write_lock :Type: raw_spinlock :Arch: x86 diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index bff78168d4a2..580ba0e86687 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2441,13 +2441,73 @@ static inline bool kvm_check_tsc_unstable(void) return check_tsc_unstable(); } +/* + * Infers attempts to synchronize the guest's tsc from host writes. Sets the + * offset for the vcpu and tracks the TSC matching generation that the vcpu + * participates in. + * + * Must hold kvm->arch.tsc_write_lock to call this function. + */ +static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc, + u64 ns, bool matched) +{ + struct kvm *kvm = vcpu->kvm; + bool already_matched; + unsigned long flags; + + lockdep_assert_held(&kvm->arch.tsc_write_lock); + + already_matched = + (vcpu->arch.this_tsc_generation == kvm->arch.cur_tsc_generation); + + /* + * We track the most recent recorded KHZ, write and time to + * allow the matching interval to be extended at each write. + */ + kvm->arch.last_tsc_nsec = ns; + kvm->arch.last_tsc_write = tsc; + kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz; + + vcpu->arch.last_guest_tsc = tsc; + + /* Keep track of which generation this VCPU has synchronized to */ + vcpu->arch.this_tsc_generation = kvm->arch.cur_tsc_generation; + vcpu->arch.this_tsc_nsec = kvm->arch.cur_tsc_nsec; + vcpu->arch.this_tsc_write = kvm->arch.cur_tsc_write; + + kvm_vcpu_write_tsc_offset(vcpu, offset); + + spin_lock_irqsave(&kvm->arch.pvclock_gtod_sync_lock, flags); + if (!matched) { + /* + * We split periods of matched TSC writes into generations. + * For each generation, we track the original measured + * nanosecond time, offset, and write, so if TSCs are in + * sync, we can match exact offset, and if not, we can match + * exact software computation in compute_guest_tsc() + * + * These values are tracked in kvm->arch.cur_xxx variables. + */ + kvm->arch.nr_vcpus_matched_tsc = 0; + kvm->arch.cur_tsc_generation++; + kvm->arch.cur_tsc_nsec = ns; + kvm->arch.cur_tsc_write = tsc; + kvm->arch.cur_tsc_offset = offset; + matched = false; + } else if (!already_matched) { + kvm->arch.nr_vcpus_matched_tsc++; + } + + kvm_track_tsc_matching(vcpu); + spin_unlock_irqrestore(&kvm->arch.pvclock_gtod_sync_lock, flags); +} + static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 data) { struct kvm *kvm = vcpu->kvm; u64 offset, ns, elapsed; unsigned long flags; - bool matched; - bool already_matched; + bool matched = false; bool synchronizing = false; raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags); @@ -2493,51 +2553,11 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 data) offset = kvm_compute_l1_tsc_offset(vcpu, data); } matched = true; - already_matched = (vcpu->arch.this_tsc_generation == kvm->arch.cur_tsc_generation); - } else { - /* - * We split periods of matched TSC writes into generations. - * For each generation, we track the original measured - * nanosecond time, offset, and write, so if TSCs are in - * sync, we can match exact offset, and if not, we can match - * exact software computation in compute_guest_tsc() - * - * These values are tracked in kvm->arch.cur_xxx variables. - */ - kvm->arch.cur_tsc_generation++; - kvm->arch.cur_tsc_nsec = ns; - kvm->arch.cur_tsc_write = data; - kvm->arch.cur_tsc_offset = offset; - matched = false; } - /* - * We also track th most recent recorded KHZ, write and time to - * allow the matching interval to be extended at each write. - */ - kvm->arch.last_tsc_nsec = ns; - kvm->arch.last_tsc_write = data; - kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz; - - vcpu->arch.last_guest_tsc = data; + __kvm_synchronize_tsc(vcpu, offset, data, ns, matched); - /* Keep track of which generation this VCPU has synchronized to */ - vcpu->arch.this_tsc_generation = kvm->arch.cur_tsc_generation; - vcpu->arch.this_tsc_nsec = kvm->arch.cur_tsc_nsec; - vcpu->arch.this_tsc_write = kvm->arch.cur_tsc_write; - - kvm_vcpu_write_tsc_offset(vcpu, offset); raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags); - - spin_lock_irqsave(&kvm->arch.pvclock_gtod_sync_lock, flags); - if (!matched) { - kvm->arch.nr_vcpus_matched_tsc = 0; - } else if (!already_matched) { - kvm->arch.nr_vcpus_matched_tsc++; - } - - kvm_track_tsc_matching(vcpu); - spin_unlock_irqrestore(&kvm->arch.pvclock_gtod_sync_lock, flags); } static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, From patchwork Mon Jul 19 18:49:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386803 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 48A03C6377C for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5D79161165 for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387879AbhGSUg3 (ORCPT ); Mon, 19 Jul 2021 16:36:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57936 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383750AbhGSSKF (ORCPT ); Mon, 19 Jul 2021 14:10:05 -0400 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 35884C0613E4 for ; Mon, 19 Jul 2021 11:38:10 -0700 (PDT) Received: by mail-qk1-x74a.google.com with SMTP id y3-20020ae9f4030000b02903b916ae903fso8690663qkl.6 for ; Mon, 19 Jul 2021 11:50:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=gVY1OcKTRkdtAsXEWyCj2ppxRNB8jvE/V1F4Gd0MP+c=; b=rIe4EYIHZTwOJ/LUiUNHR7ZP9KqycOwxusCIiH31hPQqFcVGTBc1V7P3yBAW5nLMOf rVtsH9MK/R3IUquzLnJFycOUHo+NDeEQOzM3VNusblsfRBTvJvyA9sd/uVCjpaPYgN2Z Pqzh/O7c+NxCnojD2tFCdhNGFve1zeafT1c2aRlALZVA49Yfo4japt7RG6fVIAICydTj nAVp6rKK4uOqi8/bJIJm8Lkmta2iqxjy19MO47gej1GC/N5QLDS8F2hW84QKHy1HuqZo FOPG6e4fTJXX8F2b/SlCENE/xNxwWvK6F0wJDI3/YzAyW4CkWurni2c52QqsedvFxsJ2 vwMw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=gVY1OcKTRkdtAsXEWyCj2ppxRNB8jvE/V1F4Gd0MP+c=; b=Bi+b+qrynnfN016T1sbGUkz7yz/jfJxFf/hwW6Dx0aZ0a3MfztI6UhjoGpiw3pb4RE 3PqmbKqg78W/xknRheKHGOVne4RIf1FR4loz4Gdq//pHpeHgTa68iGxJGk72CU7VIbSJ UU50SaIRSWmN3QvoKYiZgqapNVBWlfvXr6p2TPBMXvfJ2UT+F0hg7qljj2SBdtmHqxLd FP093ihX/D1pJfViRWUcwwqQqoTRvwUaOaOKXCHa2iRXbzom2Q95gAZYlF6Q6PrwDe5p nTmi8MuiBzHk+cvB8OQy/UeGsVGKNgDfhIGUMVxnsVDjS5p1RdY8YRgOP95e/xYUbWCH 2+9g== X-Gm-Message-State: AOAM533ZY6vCXQMZMhxJvPjaJ/fAibKHuwtHTG6bwfCQ2RufNrwGVpZ6 ycRsFRpeUaGT4ApkajieOhThIlEPADT97qXNjd62z84IfsTMVn79P8ZciyVF7w5kQ0/suRvF5e/ HHdsAGs8jqaQ7tr5GAY90GEQgkGEAZs5uV9eHlOaEB4ic8rQopQROlomrFw== X-Google-Smtp-Source: ABdhPJwMtLpMckHH8SI1H19lvBkJcK8ZOjmQJ0hNe6s1ExY+akygtzdlgbYmN9GT8ZpsFuaNtAn7yVUJPzI= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a05:6214:16ca:: with SMTP id d10mr25973873qvz.59.1626720603342; Mon, 19 Jul 2021 11:50:03 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:40 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-4-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 03/12] KVM: x86: Expose TSC offset controls to userspace From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org To date, VMM-directed TSC synchronization and migration has been a bit messy. KVM has some baked-in heuristics around TSC writes to infer if the VMM is attempting to synchronize. This is problematic, as it depends on host userspace writing to the guest's TSC within 1 second of the last write. A much cleaner approach to configuring the guest's views of the TSC is to simply migrate the TSC offset for every vCPU. Offsets are idempotent, and thus not subject to change depending on when the VMM actually reads/writes values from/to KVM. The VMM can then read the TSC once with KVM_GET_CLOCK to capture a (realtime, host_tsc) pair at the instant when the guest is paused. Cc: David Matlack Signed-off-by: Oliver Upton --- Documentation/virt/kvm/devices/vcpu.rst | 57 ++++++++ arch/x86/include/asm/kvm_host.h | 1 + arch/x86/include/uapi/asm/kvm.h | 4 + arch/x86/kvm/x86.c | 167 ++++++++++++++++++++++++ 4 files changed, 229 insertions(+) diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst index 2acec3b9ef65..b46d5f742e69 100644 --- a/Documentation/virt/kvm/devices/vcpu.rst +++ b/Documentation/virt/kvm/devices/vcpu.rst @@ -161,3 +161,60 @@ Specifies the base address of the stolen time structure for this VCPU. The base address must be 64 byte aligned and exist within a valid guest memory region. See Documentation/virt/kvm/arm/pvtime.rst for more information including the layout of the stolen time structure. + +4. GROUP: KVM_VCPU_TSC_CTRL +=========================== + +:Architectures: x86 + +4.1 ATTRIBUTE: KVM_VCPU_TSC_OFFSET + +:Parameters: 64-bit unsigned TSC offset + +Returns: + + ======= ====================================== + -EFAULT Error reading/writing the provided + parameter address. + -ENXIO Attribute not supported + ======= ====================================== + +Specifies the guest's TSC offset relative to the host's TSC. The guest's +TSC is then derived by the following equation: + + guest_tsc = host_tsc + KVM_VCPU_TSC_OFFSET + +This attribute is useful for the precise migration of a guest's TSC. The +following describes a possible algorithm to use for the migration of a +guest's TSC: + +From the source VMM process: + +1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (t_0), + kvmclock nanoseconds (k_0), and realtime nanoseconds (r_0). + +2. Read the KVM_VCPU_TSC_OFFSET attribute for every vCPU to record the + guest TSC offset (off_n). + +3. Invoke the KVM_GET_TSC_KHZ ioctl to record the frequency of the + guest's TSC (freq). + +From the destination VMM process: + +4. Invoke the KVM_SET_CLOCK ioctl, providing the kvmclock nanoseconds + (k_0) and realtime nanoseconds (r_0) in their respective fields. + Ensure that the KVM_CLOCK_REALTIME flag is set in the provided + structure. KVM will advance the VM's kvmclock to account for elapsed + time since recording the clock values. + +5. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (t_1) and + kvmclock nanoseconds (k_1). + +6. Adjust the guest TSC offsets for every vCPU to account for (1) time + elapsed since recording state and (2) difference in TSCs between the + source and destination machine: + + new_off_n = t_0 + off_n = (k_1 - k_0) * freq - t_1 + +7. Write the KVM_VCPU_TSC_OFFSET attribute for every vCPU with the + respective value derived in the previous step. diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 3fb2b9270d01..5f2e909b83eb 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1070,6 +1070,7 @@ struct kvm_arch { u64 last_tsc_nsec; u64 last_tsc_write; u32 last_tsc_khz; + u64 last_tsc_offset; u64 cur_tsc_nsec; u64 cur_tsc_write; u64 cur_tsc_offset; diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index a6c327f8ad9e..0b22e1e84e78 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -503,4 +503,8 @@ struct kvm_pmu_event_filter { #define KVM_PMU_EVENT_ALLOW 0 #define KVM_PMU_EVENT_DENY 1 +/* for KVM_{GET,SET,HAS}_DEVICE_ATTR */ +#define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */ +#define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */ + #endif /* _ASM_X86_KVM_H */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 580ba0e86687..9e7867410091 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2411,6 +2411,11 @@ static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) static_call(kvm_x86_write_tsc_offset)(vcpu, vcpu->arch.tsc_offset); } +static u64 kvm_vcpu_read_tsc_offset(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.l1_tsc_offset; +} + static void kvm_vcpu_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 l1_multiplier) { vcpu->arch.l1_tsc_scaling_ratio = l1_multiplier; @@ -2467,6 +2472,7 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc, kvm->arch.last_tsc_nsec = ns; kvm->arch.last_tsc_write = tsc; kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz; + kvm->arch.last_tsc_offset = offset; vcpu->arch.last_guest_tsc = tsc; @@ -4914,6 +4920,137 @@ static int kvm_set_guest_paused(struct kvm_vcpu *vcpu) return 0; } +static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int r; + + switch (attr->attr) { + case KVM_VCPU_TSC_OFFSET: + r = 0; + break; + default: + r = -ENXIO; + } + + return r; +} + +static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + void __user *uaddr = (void __user *)attr->addr; + int r; + + switch (attr->attr) { + case KVM_VCPU_TSC_OFFSET: { + u64 offset; + + offset = kvm_vcpu_read_tsc_offset(vcpu); + r = -EFAULT; + if (copy_to_user(uaddr, &offset, sizeof(offset))) + break; + + r = 0; + break; + } + default: + r = -ENXIO; + } + + return r; +} + +static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + void __user *uaddr = (void __user *)attr->addr; + struct kvm *kvm = vcpu->kvm; + int r; + + switch (attr->attr) { + case KVM_VCPU_TSC_OFFSET: { + u64 offset, tsc, ns; + unsigned long flags; + bool matched; + + r = -EFAULT; + if (copy_from_user(&offset, uaddr, sizeof(offset))) + break; + + raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags); + + matched = (vcpu->arch.virtual_tsc_khz && + kvm->arch.last_tsc_khz == vcpu->arch.virtual_tsc_khz && + kvm->arch.last_tsc_offset == offset); + + tsc = kvm_scale_tsc(vcpu, rdtsc(), vcpu->arch.l1_tsc_scaling_ratio) + offset; + ns = get_kvmclock_base_ns(); + + __kvm_synchronize_tsc(vcpu, offset, tsc, ns, matched); + raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags); + + r = 0; + break; + } + default: + r = -ENXIO; + } + + return r; +} + +static int kvm_vcpu_ioctl_has_device_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int r; + + switch (attr->group) { + case KVM_VCPU_TSC_CTRL: + r = kvm_arch_tsc_has_attr(vcpu, attr); + break; + default: + r = -ENXIO; + break; + } + + return r; +} + +static int kvm_vcpu_ioctl_get_device_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int r; + + switch (attr->group) { + case KVM_VCPU_TSC_CTRL: + r = kvm_arch_tsc_get_attr(vcpu, attr); + break; + default: + r = -ENXIO; + break; + } + + return r; +} + +static int kvm_vcpu_ioctl_set_device_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int r; + + switch (attr->group) { + case KVM_VCPU_TSC_CTRL: + r = kvm_arch_tsc_set_attr(vcpu, attr); + break; + default: + r = -ENXIO; + break; + } + + return r; +} + static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, struct kvm_enable_cap *cap) { @@ -5368,6 +5505,36 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = __set_sregs2(vcpu, u.sregs2); break; } + case KVM_HAS_DEVICE_ATTR: { + struct kvm_device_attr attr; + + r = -EFAULT; + if (copy_from_user(&attr, argp, sizeof(attr))) + goto out; + + r = kvm_vcpu_ioctl_has_device_attr(vcpu, &attr); + break; + } + case KVM_GET_DEVICE_ATTR: { + struct kvm_device_attr attr; + + r = -EFAULT; + if (copy_from_user(&attr, argp, sizeof(attr))) + goto out; + + r = kvm_vcpu_ioctl_get_device_attr(vcpu, &attr); + break; + } + case KVM_SET_DEVICE_ATTR: { + struct kvm_device_attr attr; + + r = -EFAULT; + if (copy_from_user(&attr, argp, sizeof(attr))) + goto out; + + r = kvm_vcpu_ioctl_set_device_attr(vcpu, &attr); + break; + } default: r = -EINVAL; } From patchwork Mon Jul 19 18:49:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386793 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 240A6C6379B for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7F8A661205 for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387932AbhGSUhu (ORCPT ); Mon, 19 Jul 2021 16:37:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58430 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383752AbhGSSKH (ORCPT ); Mon, 19 Jul 2021 14:10:07 -0400 Received: from mail-io1-xd49.google.com (mail-io1-xd49.google.com [IPv6:2607:f8b0:4864:20::d49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 49657C0613DB for ; Mon, 19 Jul 2021 11:38:11 -0700 (PDT) Received: by mail-io1-xd49.google.com with SMTP id c25-20020a6bec190000b02905393057ad92so1737187ioh.20 for ; Mon, 19 Jul 2021 11:50:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=hKuZx0cTa0hYyLOI0N9XgDCwLnUeCKFvR1f+63nkOr4=; b=YfmBW9QJQ4E1eUSAPYrO6dTqW4afwS4gdJJwKEDs3fkEfRpZQgCRzppR/LkZJuKMz6 KMkpU3j25Jwj8i9kH47D0ZKD7e2cbc4pg7sK6nONX6nMS/PWzoxiv8gvR65JzA/Y6Ry8 X8N70QKorlQd7xrM4UA0G4uWb+81RchqxXA983PAsTvYlIJF5fotLJmgkMgL7ze0WwIv ++IkRrG1p9tlMSpdgTKymWqegX0NSclBS+wQ59cZjTMoTLTePuV24g+H4EBQniCHwsr8 Bqr2QgQR9LSn5JO9BlqKzTqSFxMF7yOuzWhzL/eMVLlTzoNhdugxcKmM1D0WtFfdyi1a QhCA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=hKuZx0cTa0hYyLOI0N9XgDCwLnUeCKFvR1f+63nkOr4=; b=F3CuzSp6/A+b0gJUZJqvaST5HsAHE8juU8uUu5W4c0rOC5B4XgRaU7C2zAOjKYsf9F 2qk41iJ0a/4XEVGN2JQO9LSYaVIGUQrM223YJXhq/uv81SiXp0FuA5AjUEMmG5IJSNcG qbu/T5o8NncjUCO0JzY+JeljcwzqIODH2oB6EjXD1V/Nujwm+TUjl71SbPkKzpnbdqUL 4JMQN8IpKJFDUM+hBuhf5DymUQF08sY2hx4pOpoHlZx0RPwGG9bSM0ctYNjwqhFU1k4h jQqvB7BE3mcwP0f/1t+HJheDW/GOHIyQk/ydogads1RsuTwYl2vlxS3k4TYfXf+ez1fv TGGQ== X-Gm-Message-State: AOAM532e+HTxKsbUxFhJzMFAXh0KNGtUJHYqrjWmt0j/1e9N9zXKkNu+ bU+opITWJt5eyuGykkk1AoGZj9hyXS7LmaJuj7Y6MaivKcEACsiyPu83ilhuBUh93PfLXSFfuIH fga4n7GRDbjWM1FajlwVK8aWXXro6Z6fLpKkSwxEgyJlWFBM9uTwIHfE/YQ== X-Google-Smtp-Source: ABdhPJw+/Exmcm61b1jNIxXuV0uoyEg/+USrYulAOxV227xM/rDF6u0hIeC0G5Vok7FG1UdemRFXaghQYx0= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a92:d346:: with SMTP id a6mr17903215ilh.249.1626720604461; Mon, 19 Jul 2021 11:50:04 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:41 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-5-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 04/12] tools: arch: x86: pull in pvclock headers From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Copy over approximately clean versions of the pvclock headers into tools. Reconcile headers/symbols missing in tools that are unneeded. Signed-off-by: Oliver Upton --- tools/arch/x86/include/asm/pvclock-abi.h | 48 +++++++++++ tools/arch/x86/include/asm/pvclock.h | 103 +++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 tools/arch/x86/include/asm/pvclock-abi.h create mode 100644 tools/arch/x86/include/asm/pvclock.h diff --git a/tools/arch/x86/include/asm/pvclock-abi.h b/tools/arch/x86/include/asm/pvclock-abi.h new file mode 100644 index 000000000000..1436226efe3e --- /dev/null +++ b/tools/arch/x86/include/asm/pvclock-abi.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_PVCLOCK_ABI_H +#define _ASM_X86_PVCLOCK_ABI_H +#ifndef __ASSEMBLY__ + +/* + * These structs MUST NOT be changed. + * They are the ABI between hypervisor and guest OS. + * Both Xen and KVM are using this. + * + * pvclock_vcpu_time_info holds the system time and the tsc timestamp + * of the last update. So the guest can use the tsc delta to get a + * more precise system time. There is one per virtual cpu. + * + * pvclock_wall_clock references the point in time when the system + * time was zero (usually boot time), thus the guest calculates the + * current wall clock by adding the system time. + * + * Protocol for the "version" fields is: hypervisor raises it (making + * it uneven) before it starts updating the fields and raises it again + * (making it even) when it is done. Thus the guest can make sure the + * time values it got are consistent by checking the version before + * and after reading them. + */ + +struct pvclock_vcpu_time_info { + u32 version; + u32 pad0; + u64 tsc_timestamp; + u64 system_time; + u32 tsc_to_system_mul; + s8 tsc_shift; + u8 flags; + u8 pad[2]; +} __attribute__((__packed__)); /* 32 bytes */ + +struct pvclock_wall_clock { + u32 version; + u32 sec; + u32 nsec; +} __attribute__((__packed__)); + +#define PVCLOCK_TSC_STABLE_BIT (1 << 0) +#define PVCLOCK_GUEST_STOPPED (1 << 1) +/* PVCLOCK_COUNTS_FROM_ZERO broke ABI and can't be used anymore. */ +#define PVCLOCK_COUNTS_FROM_ZERO (1 << 2) +#endif /* __ASSEMBLY__ */ +#endif /* _ASM_X86_PVCLOCK_ABI_H */ diff --git a/tools/arch/x86/include/asm/pvclock.h b/tools/arch/x86/include/asm/pvclock.h new file mode 100644 index 000000000000..2628f9a6330b --- /dev/null +++ b/tools/arch/x86/include/asm/pvclock.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_PVCLOCK_H +#define _ASM_X86_PVCLOCK_H + +#include +#include + +/* some helper functions for xen and kvm pv clock sources */ +u64 pvclock_clocksource_read(struct pvclock_vcpu_time_info *src); +u8 pvclock_read_flags(struct pvclock_vcpu_time_info *src); +void pvclock_set_flags(u8 flags); +unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src); +void pvclock_resume(void); + +void pvclock_touch_watchdogs(void); + +static __always_inline +unsigned pvclock_read_begin(const struct pvclock_vcpu_time_info *src) +{ + unsigned version = src->version & ~1; + /* Make sure that the version is read before the data. */ + rmb(); + return version; +} + +static __always_inline +bool pvclock_read_retry(const struct pvclock_vcpu_time_info *src, + unsigned version) +{ + /* Make sure that the version is re-read after the data. */ + rmb(); + return version != src->version; +} + +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline u64 pvclock_scale_delta(u64 delta, u32 mul_frac, int shift) +{ + u64 product; +#ifdef __i386__ + u32 tmp1, tmp2; +#else + unsigned long tmp; +#endif + + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + +#ifdef __i386__ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "xor %5,%5 ; " + "add %4,%%eax ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); +#elif defined(__x86_64__) + __asm__ ( + "mulq %[mul_frac] ; shrd $32, %[hi], %[lo]" + : [lo]"=a"(product), + [hi]"=d"(tmp) + : "0"(delta), + [mul_frac]"rm"((u64)mul_frac)); +#else +#error implement me! +#endif + + return product; +} + +static __always_inline +u64 __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src, u64 tsc) +{ + u64 delta = tsc - src->tsc_timestamp; + u64 offset = pvclock_scale_delta(delta, src->tsc_to_system_mul, + src->tsc_shift); + return src->system_time + offset; +} + +struct pvclock_vsyscall_time_info { + struct pvclock_vcpu_time_info pvti; +} __attribute__((__aligned__(64))); + +#define PVTI_SIZE sizeof(struct pvclock_vsyscall_time_info) + +#ifdef CONFIG_PARAVIRT_CLOCK +void pvclock_set_pvti_cpu0_va(struct pvclock_vsyscall_time_info *pvti); +struct pvclock_vsyscall_time_info *pvclock_get_pvti_cpu0_va(void); +#else +static inline struct pvclock_vsyscall_time_info *pvclock_get_pvti_cpu0_va(void) +{ + return NULL; +} +#endif + +#endif /* _ASM_X86_PVCLOCK_H */ From patchwork Mon Jul 19 18:49:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386807 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-23.5 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY,USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40660C64980 for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6B15A611EF for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387901AbhGSUgt (ORCPT ); Mon, 19 Jul 2021 16:36:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58976 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383753AbhGSSKH (ORCPT ); Mon, 19 Jul 2021 14:10:07 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A61E1C0613E5 for ; Mon, 19 Jul 2021 11:38:12 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id q10-20020a056902150ab02905592911c932so26901283ybu.15 for ; Mon, 19 Jul 2021 11:50:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ZEDpe0ZAYcWsHrWESPLkcNChmXiD12rNhu+z9ayPSoQ=; b=S0k8GE9+Lo3g9spaUJzMaUW6So0qalo1Tt9AzLmD9/JTLzFb6q6AJ3uavSnWQCl2Xr UqWC2xfR/zu832YC8mBVbk0e5ZQ62hosKP28atf2GrszWnykGHHSgrwifPQ2V3wwuX5V nI1bZzZ7dRtAxMgvjJElL3+y673sRoITR9rw1aAVBORIVXma3FBOmArqwNpAyvCOhwCL EQ/XuIaCKd2wsEEgerw33BORLh3axtcFxydWnVY1aWNTyMWFQdSJF4UcU7X5he7iRBgU eLvIPrm61KFUV+P+6flpNv0qJASukinJr0WXDQAPYtKH78RdMkGY6PD9ahg22noP589L qEZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ZEDpe0ZAYcWsHrWESPLkcNChmXiD12rNhu+z9ayPSoQ=; b=gAXhnHzPwvWen3mrAsq3HutpGHywOY8UBklMflGt5V/g+sdf32z3+z5vlItMHtyRVo oE0W6Ya10HKQ1ZMZI5rVwoWGkWSaxRecuHdJQn3xHwp+A5H2f1gFfhcj2VbDLSfTPcFM ZrW3rr+WgA8i/AbuIPP6nCFqBDuOKKtkTT0s0htBeSHp48e/zi4gStK9F1y8JaoI7rt+ yCLnVqd8GXqqArSC75hVOEFAnOMjmgPHwUo1C8GH53Rjv3tI7kbTeZVTAUblhpiFPpwC RAvGk/DI4nNs1WeygCx4VXUNZRBxWZxfq81a4Tkk2MtHmnVcil5Lg5TOnp2CISS0sMBK WjXg== X-Gm-Message-State: AOAM530ppCAZDw9zdj8ia7YvlDFrI8NaRHOItc6E1Nc2Zqr70GciJxir VOxokbmaYks2fQAVGDF2fnWt92EYn9c+CqPV8uXmhePFKEuT4JV5DD9MZNfXlujNgMkI4E1i32Z uJ/9rwx8LNcaD0aq/JVQSTQBMgUT943Ree99YG1DPZPMsey+aSvb+4MXfNA== X-Google-Smtp-Source: ABdhPJwPasorex0+a8JIBXajWIF3mFLthX7cLl02P10sAgrqBmaZhhOEmDRRXkUQJgo5VByslpl71DdSvnI= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a05:6902:136e:: with SMTP id bt14mr14483392ybb.384.1626720605605; Mon, 19 Jul 2021 11:50:05 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:42 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-6-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 05/12] selftests: KVM: Add test for KVM_{GET,SET}_CLOCK From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add a selftest for the new KVM clock UAPI that was introduced. Ensure that the KVM clock is consistent between userspace and the guest, and that the difference in realtime will only ever cause the KVM clock to advance forward. Signed-off-by: Oliver Upton --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../testing/selftests/kvm/include/kvm_util.h | 2 + .../selftests/kvm/x86_64/kvm_clock_test.c | 210 ++++++++++++++++++ 4 files changed, 214 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86_64/kvm_clock_test.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 06a351b4f93b..d0877d01e771 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -11,6 +11,7 @@ /x86_64/emulator_error_test /x86_64/get_cpuid_test /x86_64/get_msr_index_features +/x86_64/kvm_clock_test /x86_64/kvm_pv_test /x86_64/hyperv_clock /x86_64/hyperv_cpuid diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index b853be2ae3c6..f7e24f334c6e 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -46,6 +46,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/get_cpuid_test TEST_GEN_PROGS_x86_64 += x86_64/hyperv_clock TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid TEST_GEN_PROGS_x86_64 += x86_64/hyperv_features +TEST_GEN_PROGS_x86_64 += x86_64/kvm_clock_test TEST_GEN_PROGS_x86_64 += x86_64/kvm_pv_test TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test TEST_GEN_PROGS_x86_64 += x86_64/mmu_role_test diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 010b59b13917..a8ac5d52e17b 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -19,6 +19,8 @@ #define KVM_DEV_PATH "/dev/kvm" #define KVM_MAX_VCPUS 512 +#define NSEC_PER_SEC 1000000000L + /* * Callers of kvm_util only have an incomplete/opaque description of the * structure kvm_util is using to maintain the state of a VM. diff --git a/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c b/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c new file mode 100644 index 000000000000..12aef5725dc4 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021, Google LLC. + * + * Tests for adjusting the KVM clock from userspace + */ +#include +#include +#include +#include +#include +#include +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +#define VCPU_ID 0 + +struct test_case { + uint64_t kvmclock_base; + int64_t realtime_offset; +}; + +static struct test_case test_cases[] = { + { .kvmclock_base = 0 }, + { .kvmclock_base = 180 * NSEC_PER_SEC }, + { .kvmclock_base = 0, .realtime_offset = -180 * NSEC_PER_SEC }, + { .kvmclock_base = 0, .realtime_offset = 180 * NSEC_PER_SEC }, +}; + +#define GUEST_SYNC_CLOCK(__stage, __val) \ + GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) + +static void guest_main(vm_paddr_t pvti_pa, struct pvclock_vcpu_time_info *pvti) +{ + int i; + + wrmsr(MSR_KVM_SYSTEM_TIME_NEW, pvti_pa | KVM_MSR_ENABLED); + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + GUEST_SYNC_CLOCK(i, __pvclock_read_cycles(pvti, rdtsc())); + } + + GUEST_DONE(); +} + +#define EXPECTED_FLAGS (KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC) + +static inline void assert_flags(struct kvm_clock_data *data) +{ + TEST_ASSERT((data->flags & EXPECTED_FLAGS) == EXPECTED_FLAGS, + "unexpected clock data flags: %x (want set: %x)", + data->flags, EXPECTED_FLAGS); +} + +static void handle_sync(struct ucall *uc, struct kvm_clock_data *start, + struct kvm_clock_data *end) +{ + uint64_t obs, exp_lo, exp_hi; + + obs = uc->args[2]; + exp_lo = start->clock; + exp_hi = end->clock; + + assert_flags(start); + assert_flags(end); + + TEST_ASSERT(exp_lo <= obs && obs <= exp_hi, + "unexpected kvm-clock value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]", + obs, exp_lo, exp_hi); + + pr_info("kvm-clock value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n", + obs, exp_lo, exp_hi); +} + +static void handle_abort(struct ucall *uc) +{ + TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], + __FILE__, uc->args[1]); +} + +static void setup_clock(struct kvm_vm *vm, struct test_case *test_case) +{ + struct kvm_clock_data data; + + memset(&data, 0, sizeof(data)); + + data.clock = test_case->kvmclock_base; + if (test_case->realtime_offset) { + struct timespec ts; + int r; + + data.flags |= KVM_CLOCK_REALTIME; + do { + r = clock_gettime(CLOCK_REALTIME, &ts); + if (!r) + break; + } while (errno == EINTR); + + TEST_ASSERT(!r, "clock_gettime() failed: %d\n", r); + + data.realtime = ts.tv_sec * NSEC_PER_SEC; + data.realtime += ts.tv_nsec; + data.realtime += test_case->realtime_offset; + } + + vm_ioctl(vm, KVM_SET_CLOCK, &data); +} + +static void enter_guest(struct kvm_vm *vm) +{ + struct kvm_clock_data start, end; + struct kvm_run *run; + struct ucall uc; + int i, r; + + run = vcpu_state(vm, VCPU_ID); + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + setup_clock(vm, &test_cases[i]); + + vm_ioctl(vm, KVM_GET_CLOCK, &start); + + r = _vcpu_run(vm, VCPU_ID); + vm_ioctl(vm, KVM_GET_CLOCK, &end); + + TEST_ASSERT(!r, "vcpu_run failed: %d\n", r); + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "unexpected exit reason: %u (%s)", + run->exit_reason, exit_reason_str(run->exit_reason)); + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_SYNC: + handle_sync(&uc, &start, &end); + break; + case UCALL_ABORT: + handle_abort(&uc); + return; + case UCALL_DONE: + return; + } + } +} + +#define CLOCKSOURCE_PATH "/sys/devices/system/clocksource/clocksource0/current_clocksource" + +static void check_clocksource(void) +{ + char *clk_name; + struct stat st; + FILE *fp; + + fp = fopen(CLOCKSOURCE_PATH, "r"); + if (!fp) { + pr_info("failed to open clocksource file: %d; assuming TSC.\n", + errno); + return; + } + + if (fstat(fileno(fp), &st)) { + pr_info("failed to stat clocksource file: %d; assuming TSC.\n", + errno); + goto out; + } + + clk_name = malloc(st.st_size); + TEST_ASSERT(clk_name, "failed to allocate buffer to read file\n"); + + if (!fgets(clk_name, st.st_size, fp)) { + pr_info("failed to read clocksource file: %d; assuming TSC.\n", + ferror(fp)); + goto out; + } + + TEST_ASSERT(!strncmp(clk_name, "tsc\n", st.st_size), + "clocksource not supported: %s", clk_name); +out: + fclose(fp); +} + +int main(void) +{ + struct kvm_cpuid2 *best; + vm_vaddr_t pvti_gva; + vm_paddr_t pvti_gpa; + struct kvm_vm *vm; + int flags; + + flags = kvm_check_cap(KVM_CAP_ADJUST_CLOCK); + if (!(flags & KVM_CLOCK_REALTIME)) { + print_skip("KVM_CLOCK_REALTIME not supported; flags: %x", + flags); + exit(KSFT_SKIP); + } + + check_clocksource(); + + vm = vm_create_default(VCPU_ID, 0, guest_main); + + best = kvm_get_supported_cpuid(); + vcpu_set_cpuid(vm, VCPU_ID, best); + + pvti_gva = vm_vaddr_alloc(vm, getpagesize(), 0x10000); + pvti_gpa = addr_gva2gpa(vm, pvti_gva); + vcpu_args_set(vm, VCPU_ID, 2, pvti_gpa, pvti_gva); + + enter_guest(vm); + kvm_vm_free(vm); +} From patchwork Mon Jul 19 18:49:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386801 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5A85CC64995 for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B7F4D6120C for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387978AbhGSUjC (ORCPT ); Mon, 19 Jul 2021 16:39:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58240 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383754AbhGSSKJ (ORCPT ); Mon, 19 Jul 2021 14:10:09 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B709EC0613E6 for ; Mon, 19 Jul 2021 11:38:13 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id h7-20020a5b0a870000b029054c59edf217so26666327ybq.3 for ; Mon, 19 Jul 2021 11:50:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=6oCR3RsSf0sSKA4Gfkr/K6wh2PtHCE6P4q0EDP+cPOQ=; b=Rs0E1+bX0JUBjrglpBzuz0teVGHiVpojaup2IZr9QAun13XkfiTisT25GYVX1NoBvR UzRc0YoYFejyTD+NvvgfL5FmG9oX3TVhKRUgTwDdBVPtAzS8DGmu6aB9P3s/tvadMZMR 5m8SxRU8nr0Xejaz/2GkzBI48N0FqHDOFWtnKxQdBCLNBV43jgo9exKLvTaW5+YP5ZJS Co9syFELKWorNMnweipBOlkAcub0Zy5XNqGL2lTwRA5NBAGOITkbcPUpdAUXT8DhO9qI hEbacZA0GlO5zGuzUC32kZ8IzOlhMu6+b9qzqnfy+yQ08JjjNy0F4itRMmM15clo7efV QO9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=6oCR3RsSf0sSKA4Gfkr/K6wh2PtHCE6P4q0EDP+cPOQ=; b=NtfFhMTQV67detiKmIHGWjOOPMsBc72VZnddyh5rXCtWH6bGsjeelmHROsV3W4K0HK enqFAdpMER4kAwn4RkdB2ltVksmFMTO50Qt4NngpV7UWSkN+op/LNfVf27jT6bdu/jSg RknSBcVqpKQR5lLn+V7QaBtNyxZ6ySk2OP9R53ROlsIJp0Afk47Ep9CxdVqkxFZ+L+kp 9Al1X8nlVrs97y0M+9D8+T3m+Lbk3nEcv9CrUNFWdYFAUw4VE6fyjN6gACyYa/2Wqidh KxjiDnsMHLG8AhHQP85V09dieMcm/U0+jqhuO8Ku0EYa3AW8HwnXvL5TY5W4/z46mQwY I/HA== X-Gm-Message-State: AOAM530wkldCUAW+ACNrp47svrgC7PZ2EdfT3Pf4P3HL1llzfI3NNaFG UcrErYgvL7HWm877NPpPbQRHOw4H0QdzlcQsLPthdcyfoRKLOHhh9SfTkcu4IOGLB/d3kl6330E 48E6aEjwno8WsThdBPHHrL2Yc19rfmftb/Cg6Q0TjYn3QuAdXEEV1CnnfIA== X-Google-Smtp-Source: ABdhPJz6ewTOkVfIXpWSdRPu0+HzbHh30XCUCUOAHHq2aNWnrTQ8xtbDRSs/M/Bhu1h/TrKSK91soYYMv7A= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a05:6902:1507:: with SMTP id q7mr34231518ybu.326.1626720606644; Mon, 19 Jul 2021 11:50:06 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:43 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-7-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 06/12] selftests: KVM: Add helpers for vCPU device attributes From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org vCPU file descriptors are abstracted away from test code in KVM selftests, meaning that tests cannot directly access a vCPU's device attributes. Add helpers that tests can use to get at vCPU device attributes. Signed-off-by: Oliver Upton --- .../testing/selftests/kvm/include/kvm_util.h | 9 +++++ tools/testing/selftests/kvm/lib/kvm_util.c | 38 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index a8ac5d52e17b..1b3ef5757819 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -240,6 +240,15 @@ int _kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, void *val, bool write); +int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr); +int vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr); +int _vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr, void *val, bool write); +int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr, void *val, bool write); + const char *exit_reason_str(unsigned int exit_reason); void virt_pgd_alloc(struct kvm_vm *vm); diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 10a8ed691c66..b595e7dc3fc5 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2040,6 +2040,44 @@ int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, return ret; } +int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr) +{ + struct vcpu *vcpu = vcpu_find(vm, vcpuid); + + TEST_ASSERT(vcpu, "nonexistent vcpu id: %d", vcpuid); + + return _kvm_device_check_attr(vcpu->fd, group, attr); +} + +int vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr) +{ + int ret = _vcpu_has_device_attr(vm, vcpuid, group, attr); + + TEST_ASSERT(!ret, "KVM_HAS_DEVICE_ATTR IOCTL failed, rc: %i errno: %i", ret, errno); + return ret; +} + +int _vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr, void *val, bool write) +{ + struct vcpu *vcpu = vcpu_find(vm, vcpuid); + + TEST_ASSERT(vcpu, "nonexistent vcpu id: %d", vcpuid); + + return _kvm_device_access(vcpu->fd, group, attr, val, write); +} + +int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, + uint64_t attr, void *val, bool write) +{ + int ret = _vcpu_access_device_attr(vm, vcpuid, group, attr, val, write); + + TEST_ASSERT(!ret, "KVM_SET|GET_DEVICE_ATTR IOCTL failed, rc: %i errno: %i", ret, errno); + return ret; +} + /* * VM Dump * From patchwork Mon Jul 19 18:49:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386795 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-23.5 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY,USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 201D7C63797 for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ABA896120A for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387965AbhGSUiq (ORCPT ); Mon, 19 Jul 2021 16:38:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383759AbhGSSKJ (ORCPT ); Mon, 19 Jul 2021 14:10:09 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D413CC0613E7 for ; Mon, 19 Jul 2021 11:38:14 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id k11-20020a056902070bb029055a2303fc2dso26770044ybt.11 for ; Mon, 19 Jul 2021 11:50:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=OVtBdE4SEQDS3BmRMBT7w6wSDOiGdRy1W//HIPm5dvA=; b=EW2fQbfqHYsIqRVs3pX7Adhz9hhvj9SG3BAfaIYoZid9btw1UIxZ9gYTgeZz8Rqx7e bQLw3TIO6uWVVB+5GOItAXVAK6wUdkh7EdPZdgropcpQKYYW+gvVvzl8vzlqJkAOmmdP MI/tD7TzZaztphS7rpqmtxLfO0v6xiwX5i/9EG6ydwSx/ut+qE1f+DsGaWv9zvksbVbx oIjlrny3DnTYJD+9E2xtggmeFaOKmgu4e3WG6r71+xFA22XW9CR0o71jrWrkkt7uugsn jrIcCqUUcPokDMXqhlr+/GZc9vTBUOelJLqaPESJR6zX0nk4fppW0r5KaLdKPTImvu6C 4xlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=OVtBdE4SEQDS3BmRMBT7w6wSDOiGdRy1W//HIPm5dvA=; b=hy30cLThDw7zs+3nl2jBlAPDqFapZoscRGtWNrYLeJKyHLPAk1SJ3Eyq6w/rcAZnH8 gC4MIbLeTCAIvOHo4dHa6cSEgapQvhoQbE8FzdXpH2ghw0utveFIbvKTJc8OVlLrscaI Xdkbdu2wgNuWQMZxemLeWJNh3EMJnFEa4y+zo0iw977O9WnDWIiOWQDXi+1CTjvKyBmP B1GsqJvuZ7XDN0nKyfq47ayNsXfB0CoKzdcNgcZXwgBVC5gRrhbwEQR7La/05pNFccU/ lDFF4cBnh6xxLvsGfBNmun1eFdyC8Jp3ZFZ3tcsYOqc4eO1yRUihHXFwgXJvODPLWIJ9 htbw== X-Gm-Message-State: AOAM533R6lJ1pM1ascZbJQlR5knpRs2GZM0oZP4UVQvs0v7peGLqQHWI LegkGFBNMb2PzCeZXIiJPaemJi3HYCgBvCYNR66cg9Q13GtTAAgPZUPVURS4tB1xitRtu9jNn2t e34LZL0EoGVyZ1vHI/ycFIZLtCTWEZeGTBLsFXzLiFXGHz9ZoIqfPpABw5A== X-Google-Smtp-Source: ABdhPJzuJjdG9Og5yhMZy1iwK0kp/5F8tiABSZCsSVX4UgMjLLzSFgiISZgLj1QZUOaWEH1viM3K8GEwKmk= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a25:ac8f:: with SMTP id x15mr34298007ybi.36.1626720607693; Mon, 19 Jul 2021 11:50:07 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:44 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-8-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 07/12] selftests: KVM: Introduce system counter offset test From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Introduce a KVM selftest to verify that userspace manipulation of the TSC (via the new vCPU attribute) results in the correct behavior within the guest. Signed-off-by: Oliver Upton --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../kvm/system_counter_offset_test.c | 133 ++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 tools/testing/selftests/kvm/system_counter_offset_test.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index d0877d01e771..2752813d5090 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -50,3 +50,4 @@ /set_memory_region_test /steal_time /kvm_binary_stats_test +/system_counter_offset_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index f7e24f334c6e..7bf2e5fb1d5a 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -83,6 +83,7 @@ TEST_GEN_PROGS_x86_64 += memslot_perf_test TEST_GEN_PROGS_x86_64 += set_memory_region_test TEST_GEN_PROGS_x86_64 += steal_time TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test +TEST_GEN_PROGS_x86_64 += system_counter_offset_test TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list diff --git a/tools/testing/selftests/kvm/system_counter_offset_test.c b/tools/testing/selftests/kvm/system_counter_offset_test.c new file mode 100644 index 000000000000..7e9015770759 --- /dev/null +++ b/tools/testing/selftests/kvm/system_counter_offset_test.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021, Google LLC. + * + * Tests for adjusting the system counter from userspace + */ +#include +#include +#include +#include +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +#define VCPU_ID 0 + +#ifdef __x86_64__ + +struct test_case { + uint64_t tsc_offset; +}; + +static struct test_case test_cases[] = { + { 0 }, + { 180 * NSEC_PER_SEC }, + { -180 * NSEC_PER_SEC }, +}; + +static void check_preconditions(struct kvm_vm *vm) +{ + if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET)) + return; + + print_skip("KVM_VCPU_TSC_OFFSET not supported; skipping test"); + exit(KSFT_SKIP); +} + +static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) +{ + vcpu_access_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, + KVM_VCPU_TSC_OFFSET, &test->tsc_offset, true); +} + +static uint64_t guest_read_system_counter(struct test_case *test) +{ + return rdtsc(); +} + +static uint64_t host_read_guest_system_counter(struct test_case *test) +{ + return rdtsc() + test->tsc_offset; +} + +#else /* __x86_64__ */ + +#error test not implemented for this architecture! + +#endif + +#define GUEST_SYNC_CLOCK(__stage, __val) \ + GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) + +static void guest_main(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + struct test_case *test = &test_cases[i]; + + GUEST_SYNC_CLOCK(i, guest_read_system_counter(test)); + } + + GUEST_DONE(); +} + +static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) +{ + uint64_t obs = uc->args[2]; + + TEST_ASSERT(start <= obs && obs <= end, + "unexpected system counter value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]", + obs, start, end); + + pr_info("system counter value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n", + obs, start, end); +} + +static void handle_abort(struct ucall *uc) +{ + TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], + __FILE__, uc->args[1]); +} + +static void enter_guest(struct kvm_vm *vm) +{ + uint64_t start, end; + struct ucall uc; + int i; + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + struct test_case *test = &test_cases[i]; + + setup_system_counter(vm, test); + start = host_read_guest_system_counter(test); + vcpu_run(vm, VCPU_ID); + end = host_read_guest_system_counter(test); + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_SYNC: + handle_sync(&uc, start, end); + break; + case UCALL_ABORT: + handle_abort(&uc); + return; + case UCALL_DONE: + return; + } + } +} + +int main(void) +{ + struct kvm_vm *vm; + + vm = vm_create_default(VCPU_ID, 0, guest_main); + check_preconditions(vm); + ucall_init(vm, NULL); + + enter_guest(vm); + kvm_vm_free(vm); +} From patchwork Mon Jul 19 18:49:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386805 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20183C63793 for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 955F261209 for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387949AbhGSUif (ORCPT ); Mon, 19 Jul 2021 16:38:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58774 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383756AbhGSSKJ (ORCPT ); Mon, 19 Jul 2021 14:10:09 -0400 Received: from mail-io1-xd49.google.com (mail-io1-xd49.google.com [IPv6:2607:f8b0:4864:20::d49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D5B92C061788 for ; Mon, 19 Jul 2021 11:38:15 -0700 (PDT) Received: by mail-io1-xd49.google.com with SMTP id h7-20020a6bb7070000b0290525efa1b760so13269611iof.16 for ; Mon, 19 Jul 2021 11:50:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=92yThg6uO5wYmeILOZOePwh93S5XAe0V6zbbybcWJO0=; b=i8aV3pLEUHpV8nXWQejniH7FEzLP8jg949SwENv5zTVoiJGTZd5TwY/1w91ERZrox0 dlN3HwtTO31IwGn6e/B3TuSCOQQm7dKyNiUV40v7hxEjbFWEcyRcZxC168KOP6k1/OfS jxKYpIBm3d2p9W8ifhUVEwp1lD83DfArX3ITyaWijicrxxCBJMTO7Fi5HTNoxzRWdCeb EE+WfmCQTV60LLuIRSFP9gaxAN7buL4cIwzvgC6gcZfzZ3hKUE+R4RsZmIWW/axj9le0 HQjD41G7SS0PeezOqVyGly61uUTLoGTrnjv0d+tQNKIRhIkJ8oBIss8deXzS95QsbtGr sc9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=92yThg6uO5wYmeILOZOePwh93S5XAe0V6zbbybcWJO0=; b=KsIrrNbZuntSOYoe1sVPx7SDlSinrbc+r7AEPhAu2PyZTrriBPRqDlVVQjIPwPtv7Q 6Q4IVEYqmiBXwhCja5nREorfC6jK5RvAwyt9IAAvxA1bzSq2AvX/MPADiacbFFPiWDbT f9JXeOXuDOKHQxs0pbQ4Ho5CXfWg6+oz8aG9nnWAq6TrhPAS+gk9tXklhBZ4f7Glz3tB hk9tLvKSNjIfiFpU7GJdC6tDe0Fg7S6Qy04GnH7AH/mpw2X1ckFcFE2HnwV6EZcp+Ihx ZLNUiTnSMOxw6EK6LRkbvB9hG01lMWMkPsoPD8DuVr3ZdkLaLPe44bZSlCSEiz4IbKhf aPog== X-Gm-Message-State: AOAM532QrBm01OTcoSl7i7Sx3bR9XGcAlC30xVKqnkkOUC11i/0FoeiC tiUoCKlUjJX5GvG9rpdj1hIIEZAi5GRJjqFa0YXGvKSzn6XlYP5gEhAgbrq5d/tDRWajLamwa9f CFRgS20RVhnp/wFddL2yXbnrQGtFOLlrHMCyT6FI+TOsdaFWPwMksG+j9Tg== X-Google-Smtp-Source: ABdhPJyUcgnGwNxKx/wbpDMOBmF7XheuCQzfCiRREH/w9XfYAuChqvaYmfRLxURgaVhKUn8EYU62XV0YxJ8= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a92:ab0a:: with SMTP id v10mr3181975ilh.17.1626720608785; Mon, 19 Jul 2021 11:50:08 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:45 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-9-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 08/12] KVM: arm64: Allow userspace to configure a vCPU's virtual offset From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add a new vCPU attribute that allows userspace to directly manipulate the virtual counter-timer offset. Exposing such an interface allows for the precise migration of guest virtual counter-timers, as it is an indepotent interface. Uphold the existing behavior of writes to CNTVOFF_EL2 for this new interface, wherein a write to a single vCPU is broadcasted to all vCPUs within a VM. Signed-off-by: Oliver Upton Reviewed-by: Andrew Jones --- Documentation/virt/kvm/devices/vcpu.rst | 22 ++++++++ arch/arm64/include/uapi/asm/kvm.h | 1 + arch/arm64/kvm/arch_timer.c | 68 ++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst index b46d5f742e69..7b57cba3416a 100644 --- a/Documentation/virt/kvm/devices/vcpu.rst +++ b/Documentation/virt/kvm/devices/vcpu.rst @@ -139,6 +139,28 @@ configured values on other VCPUs. Userspace should configure the interrupt numbers on at least one VCPU after creating all VCPUs and before running any VCPUs. +2.2. ATTRIBUTE: KVM_ARM_VCPU_TIMER_OFFSET_VTIMER +------------------------------------------------ + +:Parameters: Pointer to a 64-bit unsigned counter-timer offset. + +Returns: + + ======= ====================================== + -EFAULT Error reading/writing the provided + parameter address + -ENXIO Attribute not supported + ======= ====================================== + +Specifies the guest's virtual counter-timer offset from the host's +virtual counter. The guest's virtual counter is then derived by +the following equation: + + guest_cntvct = host_cntvct - KVM_ARM_VCPU_TIMER_OFFSET_VTIMER + +KVM does not allow the use of varying offset values for different vCPUs; +the last written offset value will be broadcasted to all vCPUs in a VM. + 3. GROUP: KVM_ARM_VCPU_PVTIME_CTRL ================================== diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index b3edde68bc3e..008d0518d2b1 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -365,6 +365,7 @@ struct kvm_arm_copy_mte_tags { #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 +#define KVM_ARM_VCPU_TIMER_OFFSET_VTIMER 2 #define KVM_ARM_VCPU_PVTIME_CTRL 2 #define KVM_ARM_VCPU_PVTIME_IPA 0 diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 3df67c127489..d2b1b13af658 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -1305,7 +1305,7 @@ static void set_timer_irqs(struct kvm *kvm, int vtimer_irq, int ptimer_irq) } } -int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +int kvm_arm_timer_set_attr_irq(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { int __user *uaddr = (int __user *)(long)attr->addr; struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); @@ -1338,7 +1338,39 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) return 0; } -int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +int kvm_arm_timer_set_attr_offset(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +{ + u64 __user *uaddr = (u64 __user *)(long)attr->addr; + u64 offset; + + if (get_user(offset, uaddr)) + return -EFAULT; + + switch (attr->attr) { + case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + update_vtimer_cntvoff(vcpu, offset); + break; + default: + return -ENXIO; + } + + return 0; +} + +int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +{ + switch (attr->attr) { + case KVM_ARM_VCPU_TIMER_IRQ_VTIMER: + case KVM_ARM_VCPU_TIMER_IRQ_PTIMER: + return kvm_arm_timer_set_attr_irq(vcpu, attr); + case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + return kvm_arm_timer_set_attr_offset(vcpu, attr); + } + + return -ENXIO; +} + +int kvm_arm_timer_get_attr_irq(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { int __user *uaddr = (int __user *)(long)attr->addr; struct arch_timer_context *timer; @@ -1359,11 +1391,43 @@ int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) return put_user(irq, uaddr); } +int kvm_arm_timer_get_attr_offset(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +{ + u64 __user *uaddr = (u64 __user *)(long)attr->addr; + struct arch_timer_context *timer; + u64 offset; + + switch (attr->attr) { + case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + timer = vcpu_vtimer(vcpu); + break; + default: + return -ENXIO; + } + + offset = timer_get_offset(timer); + return put_user(offset, uaddr); +} + +int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +{ + switch (attr->attr) { + case KVM_ARM_VCPU_TIMER_IRQ_VTIMER: + case KVM_ARM_VCPU_TIMER_IRQ_PTIMER: + return kvm_arm_timer_get_attr_irq(vcpu, attr); + case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + return kvm_arm_timer_get_attr_offset(vcpu, attr); + } + + return -ENXIO; +} + int kvm_arm_timer_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { switch (attr->attr) { case KVM_ARM_VCPU_TIMER_IRQ_VTIMER: case KVM_ARM_VCPU_TIMER_IRQ_PTIMER: + case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: return 0; } From patchwork Mon Jul 19 18:49:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386843 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3173C07E95 for ; Mon, 19 Jul 2021 21:38:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 94DA361165 for ; Mon, 19 Jul 2021 21:38:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238679AbhGSUjf (ORCPT ); Mon, 19 Jul 2021 16:39:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58986 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383760AbhGSSKJ (ORCPT ); Mon, 19 Jul 2021 14:10:09 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 29213C06178A for ; Mon, 19 Jul 2021 11:38:17 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id g3-20020a256b030000b0290551bbd99700so26748420ybc.6 for ; Mon, 19 Jul 2021 11:50:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=fvZeuFhawOmKlopyx148igwfDYKa3P5KdvR9ULck4mI=; b=TeX8LKON226q4jIrJlx/WftDAJi2pY1OEqo3ZYuEZIlxxatjAaGewz4EyaxEKG/xTD MGQgvyGFusH9M/ADz/wyUqEaAavEcB2Bbyv56OseAIZOSxSIioZgaF83g7FDtp2qmXvG BZYpoS4ToO9PSDn+w18PdbkmmCL/tls1lwW4aKbY1GuW9ZH+dzE+T2Y4jol98qPwQv/m Y6ewFtdqJnknjFuL3jhd4DOBAkRj8WhzqFi8tyELP07VXyLkB0aFG+CzQ1Pavc3ZV77J 9hmai6MbVT/A0q5jl7VxBr20c/onp/SoQzY3g+mi/e/IFVyYlRUMHXCGbhgieitI7FoX 3ezA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=fvZeuFhawOmKlopyx148igwfDYKa3P5KdvR9ULck4mI=; b=cgExmCW/XqAeWPlJ/1nCtZot5S3vm4BUCjeME4S33s64Ak1+5GGlJMwr1sXsm7LIIh aPFolilX4bJV/JwZJFgs2Z8Bq7hHuuQYPwtznNJfT62ws1vZIzQigJYkkQzWZs+48TVd LWakVUgAqknZRGsQQaVXmNzUnOLC629NW8KuMDeX96gLMhHdGLUFmvPJXYIoSUWyaT3q 17OUqHhrIw9PmatcaGXVVsTitovyHSWzTPN2Yg1i3VhXHLx375ZAS94R0upmdgUGNygJ LuUFyVu3FaMA4aqwFKcP99tBKY13aPh7Xy4VQhUzHTgmKRq2A1/P6Hb6C5Zq0aXv2n6V Zn1g== X-Gm-Message-State: AOAM533DuyVl2GGd1MANw1g6zwoJPIAz6qB/9/xorASRwPKLRo7ABrS3 UZTCQLtPZGh05Dayhqda9zP/s5VKwkbnNdh0M+semr78eYB9feF2K89HEBoLrWiALiMeUv7ZcQC uKUkjRZ70AN5RZ4h2CVSp2ZSOVfRL5d28OBWQV1LvCg/tm8l//kCIEZf+eQ== X-Google-Smtp-Source: ABdhPJyapdJfkrJhAGeUmrTv3NNW6zX92Vh5gia+jhMDC3UybLIWyyFrENJ2cGQn91HP0UTm8SnlfJC2tQc= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a25:a369:: with SMTP id d96mr33538392ybi.463.1626720609797; Mon, 19 Jul 2021 11:50:09 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:46 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-10-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 09/12] selftests: KVM: Add support for aarch64 to system_counter_offset_test From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org KVM/arm64 now allows userspace to adjust the guest virtual counter-timer via a vCPU device attribute. Test that changes to the virtual counter-timer offset result in the correct view being presented to the guest. Signed-off-by: Oliver Upton Reviewed-by: Andrew Jones --- tools/testing/selftests/kvm/Makefile | 1 + .../selftests/kvm/include/aarch64/processor.h | 12 +++++ .../kvm/system_counter_offset_test.c | 54 ++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 7bf2e5fb1d5a..d89908108c97 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -96,6 +96,7 @@ TEST_GEN_PROGS_aarch64 += kvm_page_table_test TEST_GEN_PROGS_aarch64 += set_memory_region_test TEST_GEN_PROGS_aarch64 += steal_time TEST_GEN_PROGS_aarch64 += kvm_binary_stats_test +TEST_GEN_PROGS_aarch64 += system_counter_offset_test TEST_GEN_PROGS_s390x = s390x/memop TEST_GEN_PROGS_s390x += s390x/resets diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h index 27dc5c2e56b9..3168cdbae6ee 100644 --- a/tools/testing/selftests/kvm/include/aarch64/processor.h +++ b/tools/testing/selftests/kvm/include/aarch64/processor.h @@ -129,4 +129,16 @@ void vm_install_sync_handler(struct kvm_vm *vm, #define isb() asm volatile("isb" : : : "memory") +static inline uint64_t read_cntvct_ordered(void) +{ + uint64_t r; + + __asm__ __volatile__("isb\n\t" + "mrs %0, cntvct_el0\n\t" + "isb\n\t" + : "=r"(r)); + + return r; +} + #endif /* SELFTEST_KVM_PROCESSOR_H */ diff --git a/tools/testing/selftests/kvm/system_counter_offset_test.c b/tools/testing/selftests/kvm/system_counter_offset_test.c index 7e9015770759..88ad997f5b69 100644 --- a/tools/testing/selftests/kvm/system_counter_offset_test.c +++ b/tools/testing/selftests/kvm/system_counter_offset_test.c @@ -53,7 +53,59 @@ static uint64_t host_read_guest_system_counter(struct test_case *test) return rdtsc() + test->tsc_offset; } -#else /* __x86_64__ */ +#elif __aarch64__ /* __x86_64__ */ + +enum arch_counter { + VIRTUAL, +}; + +struct test_case { + enum arch_counter counter; + uint64_t offset; +}; + +static struct test_case test_cases[] = { + { .counter = VIRTUAL, .offset = 0 }, + { .counter = VIRTUAL, .offset = 180 * NSEC_PER_SEC }, + { .counter = VIRTUAL, .offset = -180 * NSEC_PER_SEC }, +}; + +static void check_preconditions(struct kvm_vm *vm) +{ + if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_OFFSET_VTIMER)) + return; + + print_skip("KVM_ARM_VCPU_TIMER_OFFSET_VTIMER not supported; skipping test"); + exit(KSFT_SKIP); +} + +static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) +{ + vcpu_access_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_OFFSET_VTIMER, &test->offset, + true); +} + +static uint64_t guest_read_system_counter(struct test_case *test) +{ + switch (test->counter) { + case VIRTUAL: + return read_cntvct_ordered(); + default: + GUEST_ASSERT(0); + } + + /* unreachable */ + return 0; +} + +static uint64_t host_read_guest_system_counter(struct test_case *test) +{ + return read_cntvct_ordered() - test->offset; +} + +#else /* __aarch64__ */ #error test not implemented for this architecture! From patchwork Mon Jul 19 18:49:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386799 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20D91C63799 for ; Mon, 19 Jul 2021 21:20:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8A03D611CE for ; Mon, 19 Jul 2021 21:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387940AbhGSUiL (ORCPT ); Mon, 19 Jul 2021 16:38:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383758AbhGSSKJ (ORCPT ); Mon, 19 Jul 2021 14:10:09 -0400 Received: from mail-io1-xd4a.google.com (mail-io1-xd4a.google.com [IPv6:2607:f8b0:4864:20::d4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 69BB6C06178C for ; Mon, 19 Jul 2021 11:38:18 -0700 (PDT) Received: by mail-io1-xd4a.google.com with SMTP id k2-20020a5d8b020000b029050b6f9cfe31so13360345ion.11 for ; Mon, 19 Jul 2021 11:50:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=pjZrc+Mi3sbS5bI+tsGsqxmNKdHz4hcCStvsY2HBCP0=; b=ZsfWA7ksEOSjR9yWXf8VUI4vae4LLpdLIqAYD4AXrrGqDdlVxwRQrk+Ps0Aq8IVorH 4egeWpn6iokCPBScSVwrj5dEubHqYv5Su8sIFXZV0B61TiHcuzpHhHPZMyyuK3h2Y+PB T7z6KCSGv33JT8LCq2xds6OkwVhwrWTwQHaIVr7Qcf1oPHEW07WXl/qMO/VnSY4ogI5S EIqAsevUNtcaEOeeIJcn0AMDXLfNtgxvNj9Jt04lGVxOH5mawafMsafOrxFm9tuY7X5k aVWlyQBGLzbZMCF8fhSmhKhoSRXCuFbbF2/afm9bDWkf4aIXUK9Fi4GSPL3G3DNUU/6l h+zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=pjZrc+Mi3sbS5bI+tsGsqxmNKdHz4hcCStvsY2HBCP0=; b=cWNkzq3n5brAhC2aBsJMel10zU02vXwxR38xh9vfyGoS2/5Ds5oGikWs79aqaFJrQH EzmsmoJcyuIBsPLMBJRKCkemF+EdViNxhCoOr06nelbM8CBXtGgPZ4SGSHPxcOwe4fz9 55pTUK0Ma5KL9zwdRnXzHck6L/80DWyjsxSr8R85RjwPo+CrfP3Kyat7zZEvbdRqbyny 2N5YQbykm6n3pROMzTwnoZXLUpFs0zTvqL/P/pjVjDhTyJ4kvzJdEdnRBqwDoVeYwitC NkwK0Geeuu9k4z7Udm8Oo1MnJd1kjqhe2uFhIgOQLD2qu4yMSpZK8DDyPZC8olZKtYZS 9JdQ== X-Gm-Message-State: AOAM531TGnFY0XKSBSSASrVbUX2l7iTE0zLMEbfk3eTI5JXxr2e4cpDY LA9beQmUb+5us3gcuCvPTj4mktnsi1kQ05Iy37C6MIUq8hyW/orCUSLxF+PByaEV0+r+wUS5NCQ PUiLEwuVN2TnMKnNi4sdnSeXn1Ayx96I9igUCQ14a/XdFDuyEB8ECZPNr5g== X-Google-Smtp-Source: ABdhPJx0RrIgPqbzoe6/yrgR+/w3ttY4trmYHrLPuWvoKNJE1lnVSVZiW02JncD4gQJotfVnk98SNZgu8Yc= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a92:b003:: with SMTP id x3mr18117436ilh.93.1626720610823; Mon, 19 Jul 2021 11:50:10 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:47 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-11-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 10/12] KVM: arm64: Provide userspace access to the physical counter offset From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Presently, KVM provides no facilities for correctly migrating a guest that depends on the physical counter-timer. While most guests (barring NV, of course) should not depend on the physical counter-timer, an operator may still wish to provide a consistent view of the physical counter-timer across migrations. Provide userspace with a new vCPU attribute to modify the guest physical counter-timer offset. Since the base architecture doesn't provide a physical counter-timer offset register, emulate the correct behavior by trapping accesses to the physical counter-timer whenever the offset value is non-zero. Uphold the same behavior as CNTVOFF_EL2 and broadcast the physical offset to all vCPUs whenever written. This guarantees that the counter-timer we provide the guest remains architectural, wherein all views of the counter-timer are consistent across vCPUs. Reconfigure timer traps for VHE on every guest entry, as different VMs will now have different traps enabled. Enable physical counter traps for nVHE whenever the offset is nonzero (we already trap physical timer registers in nVHE). FEAT_ECV provides a guest physical counter-timer offset register (CNTPOFF_EL2), but ECV-enabled hardware is nonexistent at the time of writing so support for it was elided for the sake of the author :) Signed-off-by: Oliver Upton --- Documentation/virt/kvm/devices/vcpu.rst | 22 ++++++++++ arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/asm/kvm_hyp.h | 2 - arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/include/uapi/asm/kvm.h | 1 + arch/arm64/kvm/arch_timer.c | 50 ++++++++++++++++++++--- arch/arm64/kvm/arm.c | 4 +- arch/arm64/kvm/hyp/include/hyp/switch.h | 23 +++++++++++ arch/arm64/kvm/hyp/include/hyp/timer-sr.h | 26 ++++++++++++ arch/arm64/kvm/hyp/nvhe/switch.c | 2 - arch/arm64/kvm/hyp/nvhe/timer-sr.c | 21 +++++----- arch/arm64/kvm/hyp/vhe/timer-sr.c | 27 ++++++++++++ include/kvm/arm_arch_timer.h | 2 - 13 files changed, 158 insertions(+), 24 deletions(-) create mode 100644 arch/arm64/kvm/hyp/include/hyp/timer-sr.h diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst index 7b57cba3416a..a8547ce09b47 100644 --- a/Documentation/virt/kvm/devices/vcpu.rst +++ b/Documentation/virt/kvm/devices/vcpu.rst @@ -161,6 +161,28 @@ the following equation: KVM does not allow the use of varying offset values for different vCPUs; the last written offset value will be broadcasted to all vCPUs in a VM. +2.3. ATTRIBUTE: KVM_ARM_VCPU_TIMER_OFFSET_PTIMER +------------------------------------------------ + +:Parameters: Pointer to a 64-bit unsigned counter-timer offset. + +Returns: + + ======= ====================================== + -EFAULT Error reading/writing the provided + parameter address + -ENXIO Attribute not supported + ======= ====================================== + +Specifies the guest's physical counter-timer offset from the host's +virtual counter. The guest's physical counter is then derived by +the following equation: + + guest_cntpct = host_cntvct - KVM_ARM_VCPU_TIMER_OFFSET_PTIMER + +KVM does not allow the use of varying offset values for different vCPUs; +the last written offset value will be broadcasted to all vCPUs in a VM. + 3. GROUP: KVM_ARM_VCPU_PVTIME_CTRL ================================== diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 41911585ae0c..de92fa678924 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -204,6 +204,7 @@ enum vcpu_sysreg { SP_EL1, SPSR_EL1, + CNTPOFF_EL2, CNTVOFF_EL2, CNTV_CVAL_EL0, CNTV_CTL_EL0, diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 9d60b3006efc..01eb3864e50f 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -65,10 +65,8 @@ void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if); void __vgic_v3_restore_aprs(struct vgic_v3_cpu_if *cpu_if); int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu); -#ifdef __KVM_NVHE_HYPERVISOR__ void __timer_enable_traps(struct kvm_vcpu *vcpu); void __timer_disable_traps(struct kvm_vcpu *vcpu); -#endif #ifdef __KVM_NVHE_HYPERVISOR__ void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt); diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 347ccac2341e..243e36c088e7 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -505,6 +505,7 @@ #define SYS_AMEVCNTR0_MEM_STALL SYS_AMEVCNTR0_EL0(3) #define SYS_CNTFRQ_EL0 sys_reg(3, 3, 14, 0, 0) +#define SYS_CNTPCT_EL0 sys_reg(3, 3, 14, 0, 1) #define SYS_CNTP_TVAL_EL0 sys_reg(3, 3, 14, 2, 0) #define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1) diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 008d0518d2b1..3e42c72d4c68 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -366,6 +366,7 @@ struct kvm_arm_copy_mte_tags { #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 #define KVM_ARM_VCPU_TIMER_OFFSET_VTIMER 2 +#define KVM_ARM_VCPU_TIMER_OFFSET_PTIMER 3 #define KVM_ARM_VCPU_PVTIME_CTRL 2 #define KVM_ARM_VCPU_PVTIME_IPA 0 diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index d2b1b13af658..05ec385e26b5 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -89,7 +89,10 @@ static u64 timer_get_offset(struct arch_timer_context *ctxt) switch(arch_timer_ctx_index(ctxt)) { case TIMER_VTIMER: return __vcpu_sys_reg(vcpu, CNTVOFF_EL2); + case TIMER_PTIMER: + return __vcpu_sys_reg(vcpu, CNTPOFF_EL2); default: + WARN_ONCE(1, "unrecognized timer index %ld", arch_timer_ctx_index(ctxt)); return 0; } } @@ -134,6 +137,9 @@ static void timer_set_offset(struct arch_timer_context *ctxt, u64 offset) case TIMER_VTIMER: __vcpu_sys_reg(vcpu, CNTVOFF_EL2) = offset; break; + case TIMER_PTIMER: + __vcpu_sys_reg(vcpu, CNTPOFF_EL2) = offset; + break; default: WARN(offset, "timer %ld\n", arch_timer_ctx_index(ctxt)); } @@ -144,9 +150,24 @@ u64 kvm_phys_timer_read(void) return timecounter->cc->read(timecounter->cc); } +static bool timer_emulation_required(struct arch_timer_context *ctx) +{ + enum kvm_arch_timers timer = arch_timer_ctx_index(ctx); + + switch (timer) { + case TIMER_VTIMER: + return false; + case TIMER_PTIMER: + return timer_get_offset(ctx); + default: + WARN_ONCE(1, "unrecognized timer index %ld\n", timer); + return false; + } +} + static void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map) { - if (has_vhe()) { + if (has_vhe() && !timer_emulation_required(vcpu_ptimer(vcpu))) { map->direct_vtimer = vcpu_vtimer(vcpu); map->direct_ptimer = vcpu_ptimer(vcpu); map->emul_ptimer = NULL; @@ -747,8 +768,9 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu) return 0; } -/* Make the updates of cntvoff for all vtimer contexts atomic */ -static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff) +/* Make the updates of offset for all timer contexts atomic */ +static void update_timer_offset(struct kvm_vcpu *vcpu, + enum kvm_arch_timers timer, u64 offset) { int i; struct kvm *kvm = vcpu->kvm; @@ -756,16 +778,26 @@ static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff) mutex_lock(&kvm->lock); kvm_for_each_vcpu(i, tmp, kvm) - timer_set_offset(vcpu_vtimer(tmp), cntvoff); + timer_set_offset(vcpu_get_timer(tmp, timer), offset); /* * When called from the vcpu create path, the CPU being created is not * included in the loop above, so we just set it here as well. */ - timer_set_offset(vcpu_vtimer(vcpu), cntvoff); + timer_set_offset(vcpu_get_timer(vcpu, timer), offset); mutex_unlock(&kvm->lock); } +static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff) +{ + update_timer_offset(vcpu, TIMER_VTIMER, cntvoff); +} + +static void update_ptimer_cntpoff(struct kvm_vcpu *vcpu, u64 cntpoff) +{ + update_timer_offset(vcpu, TIMER_PTIMER, cntpoff); +} + void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = vcpu_timer(vcpu); @@ -1350,6 +1382,9 @@ int kvm_arm_timer_set_attr_offset(struct kvm_vcpu *vcpu, struct kvm_device_attr case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: update_vtimer_cntvoff(vcpu, offset); break; + case KVM_ARM_VCPU_TIMER_OFFSET_PTIMER: + update_ptimer_cntpoff(vcpu, offset); + break; default: return -ENXIO; } @@ -1364,6 +1399,7 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) case KVM_ARM_VCPU_TIMER_IRQ_PTIMER: return kvm_arm_timer_set_attr_irq(vcpu, attr); case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + case KVM_ARM_VCPU_TIMER_OFFSET_PTIMER: return kvm_arm_timer_set_attr_offset(vcpu, attr); } @@ -1400,6 +1436,8 @@ int kvm_arm_timer_get_attr_offset(struct kvm_vcpu *vcpu, struct kvm_device_attr switch (attr->attr) { case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: timer = vcpu_vtimer(vcpu); + case KVM_ARM_VCPU_TIMER_OFFSET_PTIMER: + timer = vcpu_ptimer(vcpu); break; default: return -ENXIO; @@ -1416,6 +1454,7 @@ int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) case KVM_ARM_VCPU_TIMER_IRQ_PTIMER: return kvm_arm_timer_get_attr_irq(vcpu, attr); case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + case KVM_ARM_VCPU_TIMER_OFFSET_PTIMER: return kvm_arm_timer_get_attr_offset(vcpu, attr); } @@ -1428,6 +1467,7 @@ int kvm_arm_timer_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) case KVM_ARM_VCPU_TIMER_IRQ_VTIMER: case KVM_ARM_VCPU_TIMER_IRQ_PTIMER: case KVM_ARM_VCPU_TIMER_OFFSET_VTIMER: + case KVM_ARM_VCPU_TIMER_OFFSET_PTIMER: return 0; } diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index e0b81870ff2a..217f4c6038e8 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1549,9 +1549,7 @@ static void cpu_hyp_reinit(void) cpu_hyp_reset(); - if (is_kernel_in_hyp_mode()) - kvm_timer_init_vhe(); - else + if (!is_kernel_in_hyp_mode()) cpu_init_hyp_mode(); cpu_set_hyp_vector(); diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index e4a2f295a394..c3ae1e0614a2 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -8,6 +8,7 @@ #define __ARM64_KVM_HYP_SWITCH_H__ #include +#include #include #include @@ -113,6 +114,8 @@ static inline void ___activate_traps(struct kvm_vcpu *vcpu) if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE)) write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2); + + __timer_enable_traps(vcpu); } static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) @@ -127,6 +130,8 @@ static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) vcpu->arch.hcr_el2 &= ~HCR_VSE; vcpu->arch.hcr_el2 |= read_sysreg(hcr_el2) & HCR_VSE; } + + __timer_disable_traps(vcpu); } static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar) @@ -405,6 +410,21 @@ static inline bool __hyp_handle_ptrauth(struct kvm_vcpu *vcpu) return true; } +static inline bool __hyp_handle_counter(struct kvm_vcpu *vcpu) +{ + u32 sysreg = esr_sys64_to_sysreg(kvm_vcpu_get_esr(vcpu)); + int rt = kvm_vcpu_sys_get_rt(vcpu); + u64 rv; + + if (sysreg != SYS_CNTPCT_EL0) + return false; + + rv = __timer_read_cntpct(vcpu); + vcpu_set_reg(vcpu, rt, rv); + __kvm_skip_instr(vcpu); + return true; +} + /* * Return true when we were able to fixup the guest exit and should return to * the guest, false when we should restore the host state and return to the @@ -439,6 +459,9 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code) if (*exit_code != ARM_EXCEPTION_TRAP) goto exit; + if (__hyp_handle_counter(vcpu)) + goto guest; + if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_TX2_219_TVM) && kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_SYS64 && handle_tx2_tvm(vcpu)) diff --git a/arch/arm64/kvm/hyp/include/hyp/timer-sr.h b/arch/arm64/kvm/hyp/include/hyp/timer-sr.h new file mode 100644 index 000000000000..0b0d5d1039a4 --- /dev/null +++ b/arch/arm64/kvm/hyp/include/hyp/timer-sr.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 Google LLC + * Author: Oliver Upton + */ + +#ifndef __ARM64_KVM_HYP_TIMER_SR_H__ +#define __ARM64_KVM_HYP_TIMER_SR_H__ + +#include +#include + +#include +#include + +static inline bool __timer_physical_emulation_required(struct kvm_vcpu *vcpu) +{ + return __vcpu_sys_reg(vcpu, CNTPOFF_EL2); +} + +static inline u64 __timer_read_cntpct(struct kvm_vcpu *vcpu) +{ + return read_sysreg(cntpct_el0) - __vcpu_sys_reg(vcpu, CNTPOFF_EL2); +} + +#endif /* __ARM64_KVM_HYP_TIMER_SR_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index f7af9688c1f7..4a190c932f8c 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -217,7 +217,6 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) __activate_traps(vcpu); __hyp_vgic_restore_state(vcpu); - __timer_enable_traps(vcpu); __debug_switch_to_guest(vcpu); @@ -230,7 +229,6 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) __sysreg_save_state_nvhe(guest_ctxt); __sysreg32_save_state(vcpu); - __timer_disable_traps(vcpu); __hyp_vgic_save_state(vcpu); __deactivate_traps(vcpu); diff --git a/arch/arm64/kvm/hyp/nvhe/timer-sr.c b/arch/arm64/kvm/hyp/nvhe/timer-sr.c index 9072e71693ba..ebc3f0d0908d 100644 --- a/arch/arm64/kvm/hyp/nvhe/timer-sr.c +++ b/arch/arm64/kvm/hyp/nvhe/timer-sr.c @@ -9,16 +9,13 @@ #include #include +#include void __kvm_timer_set_cntvoff(u64 cntvoff) { write_sysreg(cntvoff, cntvoff_el2); } -/* - * Should only be called on non-VHE systems. - * VHE systems use EL2 timers and configure EL1 timers in kvm_timer_init_vhe(). - */ void __timer_disable_traps(struct kvm_vcpu *vcpu) { u64 val; @@ -29,20 +26,24 @@ void __timer_disable_traps(struct kvm_vcpu *vcpu) write_sysreg(val, cnthctl_el2); } -/* - * Should only be called on non-VHE systems. - * VHE systems use EL2 timers and configure EL1 timers in kvm_timer_init_vhe(). - */ void __timer_enable_traps(struct kvm_vcpu *vcpu) { u64 val; /* * Disallow physical timer access for the guest - * Physical counter access is allowed */ val = read_sysreg(cnthctl_el2); val &= ~CNTHCTL_EL1PCEN; - val |= CNTHCTL_EL1PCTEN; + + /* + * Disallow physical counter access for the guest if offsetting is + * requested. + */ + if (__timer_physical_emulation_required(vcpu)) + val &= ~CNTHCTL_EL1PCTEN; + else + val |= CNTHCTL_EL1PCTEN; + write_sysreg(val, cnthctl_el2); } diff --git a/arch/arm64/kvm/hyp/vhe/timer-sr.c b/arch/arm64/kvm/hyp/vhe/timer-sr.c index 4cda674a8be6..10506f3ce8a1 100644 --- a/arch/arm64/kvm/hyp/vhe/timer-sr.c +++ b/arch/arm64/kvm/hyp/vhe/timer-sr.c @@ -4,9 +4,36 @@ * Author: Marc Zyngier */ +#include #include +#include void __kvm_timer_set_cntvoff(u64 cntvoff) { write_sysreg(cntvoff, cntvoff_el2); } + +void __timer_enable_traps(struct kvm_vcpu *vcpu) +{ + /* When HCR_EL2.E2H == 1, EL1PCEN nad EL1PCTEN are shifted by 10 */ + u32 cnthctl_shift = 10; + u64 val, mask; + + mask = CNTHCTL_EL1PCEN << cnthctl_shift; + mask |= CNTHCTL_EL1PCTEN << cnthctl_shift; + + val = read_sysreg(cnthctl_el2); + + /* + * VHE systems allow the guest direct access to the EL1 physical + * timer/counter if offsetting isn't requested. + */ + if (__timer_physical_emulation_required(vcpu)) + val &= ~mask; + else + val |= mask; + + write_sysreg(val, cnthctl_el2); +} + +void __timer_disable_traps(struct kvm_vcpu *vcpu) {} diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 51c19381108c..f24fc435c401 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -83,8 +83,6 @@ u64 kvm_phys_timer_read(void); void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu); void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu); -void kvm_timer_init_vhe(void); - bool kvm_arch_timer_get_input_level(int vintid); #define vcpu_timer(v) (&(v)->arch.timer_cpu) From patchwork Mon Jul 19 18:49:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386845 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 08ED7C636C9 for ; Mon, 19 Jul 2021 21:38:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C9224610FB for ; Mon, 19 Jul 2021 21:38:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1359035AbhGSUkM (ORCPT ); Mon, 19 Jul 2021 16:40:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57934 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383757AbhGSSKJ (ORCPT ); Mon, 19 Jul 2021 14:10:09 -0400 Received: from mail-oi1-x24a.google.com (mail-oi1-x24a.google.com [IPv6:2607:f8b0:4864:20::24a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F8ABC061793 for ; Mon, 19 Jul 2021 11:38:19 -0700 (PDT) Received: by mail-oi1-x24a.google.com with SMTP id u17-20020a5443910000b02902409d8d9f2fso13838700oiv.5 for ; Mon, 19 Jul 2021 11:50:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Js1tkzh0InHJDinhDdW8poSOxRWAClAIrtKK55u0+PE=; b=KUYAhBpQDsMooQkrz7c2zqbWLBcD1F57p/29Oql51TqngGSrTxE+wp2bi7H6WlPEpG 92P89yaUU/AuvB+jJXVMlSvqpqjjr04VSekq6Ex7An/nbaJaaqymAt7A+L4GmNbxWphk O9wNM44lF9ZUF0xacIlsBJ9n4+/rjQLxaNK2o0a4nYI1OeU0BtxMk6oCs6BhobrFEKrv TIYwsaDYVMZ9otssI1HiIuWC4z9qTat04n1lUCMuWtVWz7xek+ilPdS9nkS+NGd1GmiH sB/hX25SAUMnxbw4fILjiZQbUQbB6/H+iD/MltOVRChrpE1OkzQJIE5N8tvygqTbscR6 dPGw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Js1tkzh0InHJDinhDdW8poSOxRWAClAIrtKK55u0+PE=; b=LlDM7IvJ5s51mJUxKyeOPQDZhqSqzb8heLndL8Qy53ZS8YWm2skRDrDvUmmQnt+Ca+ cdSuttFJerqr+PNbNCG7Bd0aQ8pURsWDn6h1Y9AY9WDEaeLT/swQG3UBHffy8aCV1mQn exAliHw6Sq1U6MjSB1EJLv4sb7dxRFj1LecBMHsapbJhjObbxQdn0g+HAQ6rVrLzKmFb n5Ws3nyWxdGdN5LUzoFAj5/PSv+/CBQEPomfQZA7wovL/fekmYjYT7KGElE1p+BDzQDK 4aTQNUG1ssf6DXk2WwJfMWONVxWgxG4UrlNlkpVMWF5OfveC6p4WIREQEy+rbebc4Usg 4TaA== X-Gm-Message-State: AOAM5311vdKs0/yljLlIF7qju2QuGqPFVxMQoGLSC+1PYPqlRpWvldxw S1+5n4UrTc8iRbhCQ9AWprdtf0V8ojrtZCjKL3J7E6UrIYQclEO8BcxgzfT2qXNzBxDXRjRwFjR zRIwhdPr7sVUj5jk7aGyBNAMWqdFtEqU0pCAqf5R+Iv2InzqjWGczLDSikQ== X-Google-Smtp-Source: ABdhPJyU/b4AHJqg2AmHxiUKhxVMZZVmOLISQrCNLvElELkpv2SnrsueT4Y05a8t4GjE8ckwmm6/FWbJdVc= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:aca:b682:: with SMTP id g124mr18546043oif.138.1626720611974; Mon, 19 Jul 2021 11:50:11 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:48 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-12-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 11/12] selftests: KVM: Test physical counter offsetting From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Test that userpace adjustment of the guest physical counter-timer results in the correct view of within the guest. Signed-off-by: Oliver Upton Reviewed-by: Andrew Jones --- .../selftests/kvm/include/aarch64/processor.h | 12 ++++++++ .../kvm/system_counter_offset_test.c | 29 ++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h index 3168cdbae6ee..7f53d90e9512 100644 --- a/tools/testing/selftests/kvm/include/aarch64/processor.h +++ b/tools/testing/selftests/kvm/include/aarch64/processor.h @@ -141,4 +141,16 @@ static inline uint64_t read_cntvct_ordered(void) return r; } +static inline uint64_t read_cntpct_ordered(void) +{ + uint64_t r; + + __asm__ __volatile__("isb\n\t" + "mrs %0, cntpct_el0\n\t" + "isb\n\t" + : "=r"(r)); + + return r; +} + #endif /* SELFTEST_KVM_PROCESSOR_H */ diff --git a/tools/testing/selftests/kvm/system_counter_offset_test.c b/tools/testing/selftests/kvm/system_counter_offset_test.c index 88ad997f5b69..3eed9dcb7693 100644 --- a/tools/testing/selftests/kvm/system_counter_offset_test.c +++ b/tools/testing/selftests/kvm/system_counter_offset_test.c @@ -57,6 +57,7 @@ static uint64_t host_read_guest_system_counter(struct test_case *test) enum arch_counter { VIRTUAL, + PHYSICAL, }; struct test_case { @@ -68,23 +69,41 @@ static struct test_case test_cases[] = { { .counter = VIRTUAL, .offset = 0 }, { .counter = VIRTUAL, .offset = 180 * NSEC_PER_SEC }, { .counter = VIRTUAL, .offset = -180 * NSEC_PER_SEC }, + { .counter = PHYSICAL, .offset = 0 }, + { .counter = PHYSICAL, .offset = 180 * NSEC_PER_SEC }, + { .counter = PHYSICAL, .offset = -180 * NSEC_PER_SEC }, }; static void check_preconditions(struct kvm_vm *vm) { if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, - KVM_ARM_VCPU_TIMER_OFFSET_VTIMER)) + KVM_ARM_VCPU_TIMER_OFFSET_VTIMER) && + !_vcpu_has_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_OFFSET_PTIMER)) return; - print_skip("KVM_ARM_VCPU_TIMER_OFFSET_VTIMER not supported; skipping test"); + print_skip("KVM_ARM_VCPU_TIMER_OFFSET_{VTIMER,PTIMER} not supported; skipping test"); exit(KSFT_SKIP); } static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) { + u64 attr = 0; + + switch (test->counter) { + case VIRTUAL: + attr = KVM_ARM_VCPU_TIMER_OFFSET_VTIMER; + break; + case PHYSICAL: + attr = KVM_ARM_VCPU_TIMER_OFFSET_PTIMER; + break; + default: + TEST_ASSERT(false, "unrecognized counter index %u", + test->counter); + } + vcpu_access_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, - KVM_ARM_VCPU_TIMER_OFFSET_VTIMER, &test->offset, - true); + attr, &test->offset, true); } static uint64_t guest_read_system_counter(struct test_case *test) @@ -92,6 +111,8 @@ static uint64_t guest_read_system_counter(struct test_case *test) switch (test->counter) { case VIRTUAL: return read_cntvct_ordered(); + case PHYSICAL: + return read_cntpct_ordered(); default: GUEST_ASSERT(0); } From patchwork Mon Jul 19 18:49:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver Upton X-Patchwork-Id: 12386847 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1487CC12002 for ; Mon, 19 Jul 2021 21:38:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 02BF361181 for ; Mon, 19 Jul 2021 21:38:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387517AbhGSUke (ORCPT ); Mon, 19 Jul 2021 16:40:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58994 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1383761AbhGSSKL (ORCPT ); Mon, 19 Jul 2021 14:10:11 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A73AEC061794 for ; Mon, 19 Jul 2021 11:38:20 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id s186-20020a252cc30000b029055bc7fcfebdso26710924ybs.12 for ; Mon, 19 Jul 2021 11:50:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Pe/S/uzS2wg1+TTNiX12UiCpgPQMvxwQXCZew3HD/LI=; b=ocMj+e/O27vwdOyI3ks0SRsLt9P1PGSjN+lkyhm6Xi0I9EW/F9tBe56YW2aauHv/Kk asdYA/+37iI4AsZGz1imZNOcDWgyOfB/abk4bUclvgnjLc2/p8rpgMyJSSeIzlnX59qk nE4XFwmvrgk6eza4WcEm7P2XSYy6Qt8cUGm5Ijo6T1t6cfGS5mMbwjrElbU2/vUZ2COR Plv2rFVLHYsoimVkbc5C1Bn0AWAVrnuqmiIzlX9VG+8+qDJZqmLQR2BJ55lCiQAnDDTQ 4rb6oKXqd5RjaabVBxL2+S3ZU3sH90YTMvmip0epx01EthkbyBHrfJi0uSwkHSTibclA mF6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Pe/S/uzS2wg1+TTNiX12UiCpgPQMvxwQXCZew3HD/LI=; b=WZqf3H4F+aOjUQ6rXyuvIzHAVupFlgtzrupr7+e7zUka/+NxG4ElDtpVDOqORVEyvL yB0CnqHjv4Cvm8WMZOBZPC74StOKaI/OjD9ItT408NRIq6M7vImYUtW27/Z72071HtUA YBC87qbtj+YWEIy/qxIDy3wx1ADvETos1wS0HunIM8z/WyIkWA/cxrCxEr4q/gbIIGQU WJk3gCpF+QhrDZFGagfgLhMObA6b0d8Vu8lIlkBiYTpwUCwMuq4+SvHUtq4fDDYQFqUJ xbb0cGZSOLCVFk2vZEbjgWwmdAkGesA/W9WILu0nWbufk+4RrRrRR3+QJbmKPqRs0JnU GOXA== X-Gm-Message-State: AOAM533yoqS7W964S6ZEkBPMgY9vADvhRPLNomXGkZ2mc26NiF2zVUi2 N+t0487NC1KjjVfAK9N3kkAbSbEz62qefqF+0XjoJ+qB8qIB0dvLOfAD9Vh/wG9jzKlxzqO4dky mshJHpRivptn0ocviBu2BrGb+umr6Rq4ywDBLQ3kNMP/k5s3jvfmokc9nIg== X-Google-Smtp-Source: ABdhPJwOGMEIRvFFqt4m9XOjAh0/RyRXczBLXg4UlT7iKp+B6qZBSv2yzkH6ubJXWFBBQD9c7DLGzCqD1jY= X-Received: from oupton.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:404]) (user=oupton job=sendgmr) by 2002:a25:208b:: with SMTP id g133mr32514132ybg.211.1626720613054; Mon, 19 Jul 2021 11:50:13 -0700 (PDT) Date: Mon, 19 Jul 2021 18:49:49 +0000 In-Reply-To: <20210719184949.1385910-1-oupton@google.com> Message-Id: <20210719184949.1385910-13-oupton@google.com> Mime-Version: 1.0 References: <20210719184949.1385910-1-oupton@google.com> X-Mailer: git-send-email 2.32.0.402.g57bb445576-goog Subject: [PATCH v3 12/12] selftests: KVM: Add counter emulation benchmark From: Oliver Upton To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Cc: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Peter Shier , Jim Mattson , David Matlack , Ricardo Koller , Jing Zhang , Raghavendra Rao Anata , James Morse , Alexandru Elisei , Suzuki K Poulose , linux-arm-kernel@lists.infradead.org, Oliver Upton Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add a test case for counter emulation on arm64. A side effect of how KVM handles physical counter offsetting on non-ECV systems is that the virtual counter will always hit hardware and the physical could be emulated. Force emulation by writing a nonzero offset to the physical counter and compare the elapsed cycles to a direct read of the hardware register. Reviewed-by: Ricardo Koller Signed-off-by: Oliver Upton --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../kvm/aarch64/counter_emulation_benchmark.c | 215 ++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 tools/testing/selftests/kvm/aarch64/counter_emulation_benchmark.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 2752813d5090..1d811c6a769b 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only /aarch64/debug-exceptions +/aarch64/counter_emulation_benchmark /aarch64/get-reg-list /aarch64/vgic_init /s390x/memop diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index d89908108c97..e560a3e74bc2 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -86,6 +86,7 @@ TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test TEST_GEN_PROGS_x86_64 += system_counter_offset_test TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions +TEST_GEN_PROGS_aarch64 += aarch64/counter_emulation_benchmark TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list TEST_GEN_PROGS_aarch64 += aarch64/vgic_init TEST_GEN_PROGS_aarch64 += demand_paging_test diff --git a/tools/testing/selftests/kvm/aarch64/counter_emulation_benchmark.c b/tools/testing/selftests/kvm/aarch64/counter_emulation_benchmark.c new file mode 100644 index 000000000000..73aeb6cdebfe --- /dev/null +++ b/tools/testing/selftests/kvm/aarch64/counter_emulation_benchmark.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * counter_emulation_benchmark.c -- test to measure the effects of counter + * emulation on guest reads of the physical counter. + * + * Copyright (c) 2021, Google LLC. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "kvm_util.h" +#include "processor.h" +#include "test_util.h" + +#define VCPU_ID 0 + +static struct counter_values { + uint64_t cntvct_start; + uint64_t cntpct; + uint64_t cntvct_end; +} counter_values; + +static uint64_t nr_iterations = 1000; + +static void do_test(void) +{ + /* + * Open-coded approach instead of using helper methods to keep a tight + * interval around the physical counter read. + */ + asm volatile("isb\n\t" + "mrs %[cntvct_start], cntvct_el0\n\t" + "isb\n\t" + "mrs %[cntpct], cntpct_el0\n\t" + "isb\n\t" + "mrs %[cntvct_end], cntvct_el0\n\t" + "isb\n\t" + : [cntvct_start] "=r"(counter_values.cntvct_start), + [cntpct] "=r"(counter_values.cntpct), + [cntvct_end] "=r"(counter_values.cntvct_end)); +} + +static void guest_main(void) +{ + int i; + + for (i = 0; i < nr_iterations; i++) { + do_test(); + GUEST_SYNC(i); + } + + for (i = 0; i < nr_iterations; i++) { + do_test(); + GUEST_SYNC(i); + } + + GUEST_DONE(); +} + +static bool enter_guest(struct kvm_vm *vm) +{ + struct ucall uc; + + vcpu_ioctl(vm, VCPU_ID, KVM_RUN, NULL); + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_DONE: + return true; + case UCALL_SYNC: + break; + case UCALL_ABORT: + TEST_ASSERT(false, "%s at %s:%ld", (const char *)uc.args[0], + __FILE__, uc.args[1]); + break; + default: + TEST_ASSERT(false, "unexpected exit: %s", + exit_reason_str(vcpu_state(vm, VCPU_ID)->exit_reason)); + break; + } + + /* more work to do in the guest */ + return false; +} + +static double counter_frequency(void) +{ + uint32_t freq; + + asm volatile("mrs %0, cntfrq_el0" + : "=r" (freq)); + + return freq / 1000000.0; +} + +static void log_csv(FILE *csv, bool trapped) +{ + double freq = counter_frequency(); + + fprintf(csv, "%s,%.02f,%lu,%lu,%lu\n", + trapped ? "true" : "false", freq, + counter_values.cntvct_start, + counter_values.cntpct, + counter_values.cntvct_end); +} + +static double run_loop(struct kvm_vm *vm, FILE *csv, bool trapped) +{ + double avg = 0; + int i; + + for (i = 0; i < nr_iterations; i++) { + uint64_t delta; + + TEST_ASSERT(!enter_guest(vm), "guest exited unexpectedly"); + sync_global_from_guest(vm, counter_values); + + if (csv) + log_csv(csv, trapped); + + delta = counter_values.cntvct_end - counter_values.cntvct_start; + avg = ((avg * i) + delta) / (i + 1); + } + + return avg; +} + +static void setup_counter(struct kvm_vm *vm, uint64_t offset) +{ + vcpu_access_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_OFFSET_PTIMER, &offset, + true); +} + +static void run_tests(struct kvm_vm *vm, FILE *csv) +{ + double avg_trapped, avg_native, freq; + + freq = counter_frequency(); + + if (csv) + fputs("trapped,freq_mhz,cntvct_start,cntpct,cntvct_end\n", csv); + + /* no physical offsetting; kvm allows reads of cntpct_el0 */ + setup_counter(vm, 0); + avg_native = run_loop(vm, csv, false); + + /* force emulation of the physical counter */ + setup_counter(vm, 1); + avg_trapped = run_loop(vm, csv, true); + + TEST_ASSERT(enter_guest(vm), "guest didn't run to completion"); + pr_info("%lu iterations: average cycles (@%.02fMHz) native: %.02f, trapped: %.02f\n", + nr_iterations, freq, avg_native, avg_trapped); +} + +static void usage(const char *program_name) +{ + fprintf(stderr, + "Usage: %s [-h] [-o csv_file] [-n iterations]\n" + " -h prints this message\n" + " -n number of test iterations (default: %lu)\n" + " -o csv file to write data\n", + program_name, nr_iterations); +} + +int main(int argc, char **argv) +{ + struct kvm_vm *vm; + FILE *csv = NULL; + int opt; + + while ((opt = getopt(argc, argv, "hn:o:")) != -1) { + switch (opt) { + case 'o': + csv = fopen(optarg, "w"); + if (!csv) { + fprintf(stderr, "failed to open file '%s': %d\n", + optarg, errno); + exit(1); + } + break; + case 'n': + nr_iterations = strtoul(optarg, NULL, 0); + break; + default: + fprintf(stderr, "unrecognized option: '-%c'\n", opt); + /* fallthrough */ + case 'h': + usage(argv[0]); + exit(1); + } + } + + vm = vm_create_default(VCPU_ID, 0, guest_main); + sync_global_to_guest(vm, nr_iterations); + ucall_init(vm, NULL); + + if (_vcpu_has_device_attr(vm, VCPU_ID, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_OFFSET_PTIMER)) { + print_skip("KVM_ARM_VCPU_TIMER_OFFSET_PTIMER not supported."); + exit(KSFT_SKIP); + } + + run_tests(vm, csv); + kvm_vm_free(vm); + + if (csv) + fclose(csv); +}