Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.10 197 lines 4.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Virtual PTP 1588 clock for use with KVM guests 4 * 5 * Copyright (C) 2017 Red Hat Inc. 6 */ 7#include <linux/device.h> 8#include <linux/err.h> 9#include <linux/init.h> 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <uapi/linux/kvm_para.h> 13#include <asm/kvm_para.h> 14#include <asm/pvclock.h> 15#include <asm/kvmclock.h> 16#include <uapi/asm/kvm_para.h> 17 18#include <linux/ptp_clock_kernel.h> 19 20struct kvm_ptp_clock { 21 struct ptp_clock *ptp_clock; 22 struct ptp_clock_info caps; 23}; 24 25static DEFINE_SPINLOCK(kvm_ptp_lock); 26 27static struct pvclock_vsyscall_time_info *hv_clock; 28 29static struct kvm_clock_pairing clock_pair; 30static phys_addr_t clock_pair_gpa; 31 32static int ptp_kvm_get_time_fn(ktime_t *device_time, 33 struct system_counterval_t *system_counter, 34 void *ctx) 35{ 36 unsigned long ret; 37 struct timespec64 tspec; 38 unsigned version; 39 int cpu; 40 struct pvclock_vcpu_time_info *src; 41 42 spin_lock(&kvm_ptp_lock); 43 44 preempt_disable_notrace(); 45 cpu = smp_processor_id(); 46 src = &hv_clock[cpu].pvti; 47 48 do { 49 /* 50 * We are using a TSC value read in the hosts 51 * kvm_hc_clock_pairing handling. 52 * So any changes to tsc_to_system_mul 53 * and tsc_shift or any other pvclock 54 * data invalidate that measurement. 55 */ 56 version = pvclock_read_begin(src); 57 58 ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 59 clock_pair_gpa, 60 KVM_CLOCK_PAIRING_WALLCLOCK); 61 if (ret != 0) { 62 pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); 63 spin_unlock(&kvm_ptp_lock); 64 preempt_enable_notrace(); 65 return -EOPNOTSUPP; 66 } 67 68 tspec.tv_sec = clock_pair.sec; 69 tspec.tv_nsec = clock_pair.nsec; 70 ret = __pvclock_read_cycles(src, clock_pair.tsc); 71 } while (pvclock_read_retry(src, version)); 72 73 preempt_enable_notrace(); 74 75 system_counter->cycles = ret; 76 system_counter->cs = &kvm_clock; 77 78 *device_time = timespec64_to_ktime(tspec); 79 80 spin_unlock(&kvm_ptp_lock); 81 82 return 0; 83} 84 85static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp, 86 struct system_device_crosststamp *xtstamp) 87{ 88 return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL, 89 NULL, xtstamp); 90} 91 92/* 93 * PTP clock operations 94 */ 95 96static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 97{ 98 return -EOPNOTSUPP; 99} 100 101static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta) 102{ 103 return -EOPNOTSUPP; 104} 105 106static int ptp_kvm_settime(struct ptp_clock_info *ptp, 107 const struct timespec64 *ts) 108{ 109 return -EOPNOTSUPP; 110} 111 112static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 113{ 114 unsigned long ret; 115 struct timespec64 tspec; 116 117 spin_lock(&kvm_ptp_lock); 118 119 ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 120 clock_pair_gpa, 121 KVM_CLOCK_PAIRING_WALLCLOCK); 122 if (ret != 0) { 123 pr_err_ratelimited("clock offset hypercall ret %lu\n", ret); 124 spin_unlock(&kvm_ptp_lock); 125 return -EOPNOTSUPP; 126 } 127 128 tspec.tv_sec = clock_pair.sec; 129 tspec.tv_nsec = clock_pair.nsec; 130 spin_unlock(&kvm_ptp_lock); 131 132 memcpy(ts, &tspec, sizeof(struct timespec64)); 133 134 return 0; 135} 136 137static int ptp_kvm_enable(struct ptp_clock_info *ptp, 138 struct ptp_clock_request *rq, int on) 139{ 140 return -EOPNOTSUPP; 141} 142 143static const struct ptp_clock_info ptp_kvm_caps = { 144 .owner = THIS_MODULE, 145 .name = "KVM virtual PTP", 146 .max_adj = 0, 147 .n_ext_ts = 0, 148 .n_pins = 0, 149 .pps = 0, 150 .adjfreq = ptp_kvm_adjfreq, 151 .adjtime = ptp_kvm_adjtime, 152 .gettime64 = ptp_kvm_gettime, 153 .settime64 = ptp_kvm_settime, 154 .enable = ptp_kvm_enable, 155 .getcrosststamp = ptp_kvm_getcrosststamp, 156}; 157 158/* module operations */ 159 160static struct kvm_ptp_clock kvm_ptp_clock; 161 162static void __exit ptp_kvm_exit(void) 163{ 164 ptp_clock_unregister(kvm_ptp_clock.ptp_clock); 165} 166 167static int __init ptp_kvm_init(void) 168{ 169 long ret; 170 171 if (!kvm_para_available()) 172 return -ENODEV; 173 174 clock_pair_gpa = slow_virt_to_phys(&clock_pair); 175 hv_clock = pvclock_get_pvti_cpu0_va(); 176 177 if (!hv_clock) 178 return -ENODEV; 179 180 ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, 181 KVM_CLOCK_PAIRING_WALLCLOCK); 182 if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP) 183 return -ENODEV; 184 185 kvm_ptp_clock.caps = ptp_kvm_caps; 186 187 kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); 188 189 return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock); 190} 191 192module_init(ptp_kvm_init); 193module_exit(ptp_kvm_exit); 194 195MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>"); 196MODULE_DESCRIPTION("PTP clock using KVMCLOCK"); 197MODULE_LICENSE("GPL");