From: lisiheng lisiheng@bingosoft.net
PTP_KVM is used for high precision time sync between host and guests. It relies on transferring the wall clock and counter value from the host to the guest using a KVM-specific hypercall.
mainline inclusion from mainline-v4.19 category: feature bugzilla:https://gitee.com/openeuler/kernel/issues/I3R900 CVE: NA
Reference: https://www.kernel.org/doc/html/latest/_sources/virt/kvm/arm/ptp_kvm.rst.txt https://github.com/kata-containers/packaging/blob/master/kernel/patches/4.19... Signed-off-by: lisiheng lisiheng@bingosoft.net --- drivers/clocksource/arm_arch_timer.c | 24 ++++++ drivers/ptp/Kconfig | 2 +- drivers/ptp/Makefile | 1 + drivers/ptp/ptp_kvm_arm64.c | 52 ++++++++++++ drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} | 89 +++++---------------- drivers/ptp/ptp_kvm_x86.c | 87 ++++++++++++++++++++ fs/jbd2/transaction.c | 17 ---- include/asm-generic/ptp_kvm.h | 12 +++ include/linux/arm-smccc.h | 5 ++ virt/kvm/arm/hypercalls.c | 14 ++++ 10 files changed, 216 insertions(+), 87 deletions(-) create mode 100644 drivers/ptp/ptp_kvm_arm64.c rename drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} (55%) create mode 100644 drivers/ptp/ptp_kvm_x86.c create mode 100644 include/asm-generic/ptp_kvm.h
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 6847a5fe13fd..f84bed34e9ea 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -1653,3 +1653,27 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table) } TIMER_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init); #endif + +#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_KVM) +#include <linux/arm-smccc.h> +int kvm_arch_ptp_get_clock_fn(long *cycle, struct timespec64 *ts, + struct clocksource **cs) +{ + struct arm_smccc_res hvc_res; + + arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, + &hvc_res); + + if ((long)(hvc_res.a0) < 0) + return -EOPNOTSUPP; + + ts->tv_sec = hvc_res.a0; + ts->tv_nsec = hvc_res.a1; + *cycle = hvc_res.a2 << 32 | hvc_res.a3; + *cs = &clocksource_counter; + + return 0; +} +EXPORT_SYMBOL_GPL(kvm_arch_ptp_get_clock_fn); +#endif + diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index d137c480db46..318b3f5df1ea 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -109,7 +109,7 @@ config PTP_1588_CLOCK_PCH config PTP_1588_CLOCK_KVM tristate "KVM virtual PTP clock" depends on PTP_1588_CLOCK - depends on KVM_GUEST && X86 + depends on KVM_GUEST && X86 || ARM64 default y help This driver adds support for using kvm infrastructure as a PTP diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile index 19efa9cfa950..1bf4940a88a6 100644 --- a/drivers/ptp/Makefile +++ b/drivers/ptp/Makefile @@ -4,6 +4,7 @@ #
ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o +ptp_kvm-y := ptp_kvm_common.o ptp_kvm_$(ARCH).o obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o diff --git a/drivers/ptp/ptp_kvm_arm64.c b/drivers/ptp/ptp_kvm_arm64.c new file mode 100644 index 000000000000..07ee2fc2c7b1 --- /dev/null +++ b/drivers/ptp/ptp_kvm_arm64.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Virtual PTP 1588 clock for use with KVM guests + * Copyright (C) 2019 ARM Ltd. + * All Rights Reserved + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <asm/hypervisor.h> +#include <linux/module.h> +#include <linux/psci.h> +#include <linux/arm-smccc.h> +#include <linux/timecounter.h> +#include <linux/sched/clock.h> +#include <asm/arch_timer.h> + + +int kvm_arch_ptp_init(void) +{ + struct arm_smccc_res hvc_res; + + arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, + &hvc_res); + if ((long)(hvc_res.a0) < 0) + return -EOPNOTSUPP; + + return 0; +} + +int kvm_arch_ptp_get_clock_generic(struct timespec64 *ts, + struct arm_smccc_res *hvc_res) +{ + arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, + hvc_res); + if ((long)(hvc_res->a0) < 0) + return -EOPNOTSUPP; + + ts->tv_sec = hvc_res->a0; + ts->tv_nsec = hvc_res->a1; + + return 0; +} + +int kvm_arch_ptp_get_clock(struct timespec64 *ts) +{ + struct arm_smccc_res hvc_res; + + kvm_arch_ptp_get_clock_generic(ts, &hvc_res); + + return 0; +} diff --git a/drivers/ptp/ptp_kvm.c b/drivers/ptp/ptp_kvm_common.c similarity index 55% rename from drivers/ptp/ptp_kvm.c rename to drivers/ptp/ptp_kvm_common.c index c67dd11e08b1..69a7cd05d188 100644 --- a/drivers/ptp/ptp_kvm.c +++ b/drivers/ptp/ptp_kvm_common.c @@ -1,29 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Virtual PTP 1588 clock for use with KVM guests * * Copyright (C) 2017 Red Hat Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/device.h> #include <linux/err.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/module.h> #include <uapi/linux/kvm_para.h> #include <asm/kvm_para.h> -#include <asm/pvclock.h> -#include <asm/kvmclock.h> -#include <uapi/asm/kvm_para.h> +#include <asm-generic/ptp_kvm.h>
#include <linux/ptp_clock_kernel.h>
@@ -34,56 +24,31 @@ struct kvm_ptp_clock {
DEFINE_SPINLOCK(kvm_ptp_lock);
-static struct pvclock_vsyscall_time_info *hv_clock; - -static struct kvm_clock_pairing clock_pair; -static phys_addr_t clock_pair_gpa;
static int ptp_kvm_get_time_fn(ktime_t *device_time, struct system_counterval_t *system_counter, void *ctx) { - unsigned long ret; + unsigned long ret, cycle; struct timespec64 tspec; - unsigned version; - int cpu; - struct pvclock_vcpu_time_info *src; + struct clocksource *cs;
spin_lock(&kvm_ptp_lock);
preempt_disable_notrace(); - cpu = smp_processor_id(); - src = &hv_clock[cpu].pvti; - - do { - /* - * We are using a TSC value read in the hosts - * kvm_hc_clock_pairing handling. - * So any changes to tsc_to_system_mul - * and tsc_shift or any other pvclock - * data invalidate that measurement. - */ - version = pvclock_read_begin(src); - - ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, - clock_pair_gpa, - KVM_CLOCK_PAIRING_WALLCLOCK); - if (ret != 0) { - pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); - spin_unlock(&kvm_ptp_lock); - preempt_enable_notrace(); - return -EOPNOTSUPP; - } - - tspec.tv_sec = clock_pair.sec; - tspec.tv_nsec = clock_pair.nsec; - ret = __pvclock_read_cycles(src, clock_pair.tsc); - } while (pvclock_read_retry(src, version)); + + ret = kvm_arch_ptp_get_clock_fn(&cycle, &tspec, &cs); + if (ret != 0) { + pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); + spin_unlock(&kvm_ptp_lock); + preempt_enable_notrace(); + return -EOPNOTSUPP; + }
preempt_enable_notrace();
- system_counter->cycles = ret; - system_counter->cs = &kvm_clock; + system_counter->cycles = cycle; + system_counter->cs = cs;
*device_time = timespec64_to_ktime(tspec);
@@ -126,17 +91,13 @@ static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
spin_lock(&kvm_ptp_lock);
- ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, - clock_pair_gpa, - KVM_CLOCK_PAIRING_WALLCLOCK); + ret = kvm_arch_ptp_get_clock(&tspec); if (ret != 0) { pr_err_ratelimited("clock offset hypercall ret %lu\n", ret); spin_unlock(&kvm_ptp_lock); return -EOPNOTSUPP; }
- tspec.tv_sec = clock_pair.sec; - tspec.tv_nsec = clock_pair.nsec; spin_unlock(&kvm_ptp_lock);
memcpy(ts, &tspec, sizeof(struct timespec64)); @@ -176,22 +137,12 @@ static void __exit ptp_kvm_exit(void)
static int __init ptp_kvm_init(void) { - long ret; - - if (!kvm_para_available()) - return -ENODEV;
- clock_pair_gpa = slow_virt_to_phys(&clock_pair); - hv_clock = pvclock_get_pvti_cpu0_va(); - - if (!hv_clock) - return -ENODEV; - - ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, - KVM_CLOCK_PAIRING_WALLCLOCK); - if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP) - return -ENODEV; + int ret;
+ ret = kvm_arch_ptp_init(); + if (ret) + return -EOPNOTSUPP; kvm_ptp_clock.caps = ptp_kvm_caps;
kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); diff --git a/drivers/ptp/ptp_kvm_x86.c b/drivers/ptp/ptp_kvm_x86.c new file mode 100644 index 000000000000..a52cf1c2990c --- /dev/null +++ b/drivers/ptp/ptp_kvm_x86.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Virtual PTP 1588 clock for use with KVM guests + * + * Copyright (C) 2017 Red Hat Inc. + */ + +#include <asm/pvclock.h> +#include <asm/kvmclock.h> +#include <linux/module.h> +#include <uapi/asm/kvm_para.h> +#include <uapi/linux/kvm_para.h> +#include <linux/ptp_clock_kernel.h> + +phys_addr_t clock_pair_gpa; +struct kvm_clock_pairing clock_pair; +struct pvclock_vsyscall_time_info *hv_clock; + +int kvm_arch_ptp_init(void) +{ + int ret; + + if (!kvm_para_available()) + return -ENODEV; + + clock_pair_gpa = slow_virt_to_phys(&clock_pair); + hv_clock = pvclock_get_pvti_cpu0_va(); + if (!hv_clock) + return -ENODEV; + + ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, + KVM_CLOCK_PAIRING_WALLCLOCK); + if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP) + return -ENODEV; + + return 0; +} + +int kvm_arch_ptp_get_clock(struct timespec64 *ts) +{ + long ret; + + ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, + clock_pair_gpa, + KVM_CLOCK_PAIRING_WALLCLOCK); + if (ret != 0) + return -EOPNOTSUPP; + + ts->tv_sec = clock_pair.sec; + ts->tv_nsec = clock_pair.nsec; + + return 0; +} + +int kvm_arch_ptp_get_clock_fn(unsigned long *cycle, struct timespec64 *tspec, + struct clocksource **cs) +{ + unsigned long ret; + unsigned int version; + int cpu; + struct pvclock_vcpu_time_info *src; + + cpu = smp_processor_id(); + src = &hv_clock[cpu].pvti; + + do { + /* + * We are using a TSC value read in the hosts + * kvm_hc_clock_pairing handling. + * So any changes to tsc_to_system_mul + * and tsc_shift or any other pvclock + * data invalidate that measurement. + */ + version = pvclock_read_begin(src); + + ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, + clock_pair_gpa, + KVM_CLOCK_PAIRING_WALLCLOCK); + tspec->tv_sec = clock_pair.sec; + tspec->tv_nsec = clock_pair.nsec; + *cycle = __pvclock_read_cycles(src, clock_pair.tsc); + } while (pvclock_read_retry(src, version)); + + *cs = &kvm_clock; + + return 0; +} diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index ffa6d3530f4b..4055929a043c 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -2085,7 +2085,6 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal, { struct buffer_head *head; struct buffer_head *bh; - bool has_write_io_error = false; int ret = 0;
J_ASSERT(PageLocked(page)); @@ -2110,26 +2109,10 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal, jbd_unlock_bh_state(bh); if (buffer_jbd(bh)) goto busy; - - /* - * If we free a metadata buffer which has been failed to - * write out, the jbd2 checkpoint procedure will not detect - * this failure and may lead to filesystem inconsistency - * after cleanup journal tail. - */ - if (buffer_write_io_error(bh)) { - pr_err("JBD2: Error while async write back metadata bh %llu.", - (unsigned long long)bh->b_blocknr); - has_write_io_error = true; - } } while ((bh = bh->b_this_page) != head);
ret = try_to_free_buffers(page); - busy: - if (has_write_io_error) - jbd2_journal_abort(journal, -EIO); - return ret; }
diff --git a/include/asm-generic/ptp_kvm.h b/include/asm-generic/ptp_kvm.h new file mode 100644 index 000000000000..d87a08495710 --- /dev/null +++ b/include/asm-generic/ptp_kvm.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/clocksource/arm_arch_timer.c + * + * Copyright (C) 2019 ARM Ltd. + * All Rights Reserved + */ + +int kvm_arch_ptp_init(void); +int kvm_arch_ptp_get_clock(struct timespec64 *ts); +int kvm_arch_ptp_get_clock_fn(unsigned long *cycle, + struct timespec64 *tspec, void *cs); diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 5b5f52ca6ac8..5a46bb6df894 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -76,6 +76,11 @@ ARM_SMCCC_SMC_32, \ 0, 1)
+#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + 0, 2) + #define ARM_SMCCC_ARCH_WORKAROUND_1 \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ ARM_SMCCC_SMC_32, \ diff --git a/virt/kvm/arm/hypercalls.c b/virt/kvm/arm/hypercalls.c index 7a6c5f18dff2..495b25b9ed13 100644 --- a/virt/kvm/arm/hypercalls.c +++ b/virt/kvm/arm/hypercalls.c @@ -15,6 +15,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) u32 val = SMCCC_RET_NOT_SUPPORTED; u32 feature; gpa_t gpa; + struct timespec64 ts; + u64 cycles, cycle_high, cycle_low; + struct system_time_snapshot systime_snapshot;
switch (func_id) { case ARM_SMCCC_VERSION_FUNC_ID: @@ -63,6 +66,17 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) case ARM_SMCCC_HV_PV_SCHED_KICK_CPU: val = kvm_pvsched_kick_vcpu(vcpu); break; + + case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID: + ktime_get_real_ts64(&ts); + ktime_get_snapshot(&systime_snapshot); + cycles = systime_snapshot.cycles - vcpu_vtimer(vcpu)->cntvoff; + cycle_high = cycles >> 32; + cycle_low = cycles << 32 >> 32; + + smccc_set_retval(vcpu, ts.tv_sec, ts.tv_nsec, cycle_high, cycle_low); + return 1; + default: return kvm_psci_call(vcpu); }