Branch data Line data Source code
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 : :
20 : : struct kvm_ptp_clock {
21 : : struct ptp_clock *ptp_clock;
22 : : struct ptp_clock_info caps;
23 : : };
24 : :
25 : : DEFINE_SPINLOCK(kvm_ptp_lock);
26 : :
27 : : static struct pvclock_vsyscall_time_info *hv_clock;
28 : :
29 : : static struct kvm_clock_pairing clock_pair;
30 : : static phys_addr_t clock_pair_gpa;
31 : :
32 : 0 : static int ptp_kvm_get_time_fn(ktime_t *device_time,
33 : : struct system_counterval_t *system_counter,
34 : : void *ctx)
35 : : {
36 : 0 : unsigned long ret;
37 : 0 : struct timespec64 tspec;
38 : 0 : unsigned version;
39 : 0 : int cpu;
40 : 0 : struct pvclock_vcpu_time_info *src;
41 : :
42 : 0 : spin_lock(&kvm_ptp_lock);
43 : :
44 : 0 : preempt_disable_notrace();
45 : 0 : cpu = smp_processor_id();
46 : 0 : src = &hv_clock[cpu].pvti;
47 : :
48 : 0 : 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 : 0 : version = pvclock_read_begin(src);
57 : :
58 : 0 : ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
59 : : clock_pair_gpa,
60 : : KVM_CLOCK_PAIRING_WALLCLOCK);
61 [ # # ]: 0 : if (ret != 0) {
62 [ # # ]: 0 : pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret);
63 : 0 : spin_unlock(&kvm_ptp_lock);
64 : 0 : preempt_enable_notrace();
65 : 0 : return -EOPNOTSUPP;
66 : : }
67 : :
68 : 0 : tspec.tv_sec = clock_pair.sec;
69 : 0 : tspec.tv_nsec = clock_pair.nsec;
70 [ # # ]: 0 : ret = __pvclock_read_cycles(src, clock_pair.tsc);
71 [ # # ]: 0 : } while (pvclock_read_retry(src, version));
72 : :
73 : 0 : preempt_enable_notrace();
74 : :
75 : 0 : system_counter->cycles = ret;
76 : 0 : system_counter->cs = &kvm_clock;
77 : :
78 [ # # ]: 0 : *device_time = timespec64_to_ktime(tspec);
79 : :
80 : 0 : spin_unlock(&kvm_ptp_lock);
81 : :
82 : 0 : return 0;
83 : : }
84 : :
85 : 0 : static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp,
86 : : struct system_device_crosststamp *xtstamp)
87 : : {
88 : 0 : return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL,
89 : : NULL, xtstamp);
90 : : }
91 : :
92 : : /*
93 : : * PTP clock operations
94 : : */
95 : :
96 : 0 : static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
97 : : {
98 : 0 : return -EOPNOTSUPP;
99 : : }
100 : :
101 : 0 : static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta)
102 : : {
103 : 0 : return -EOPNOTSUPP;
104 : : }
105 : :
106 : 0 : static int ptp_kvm_settime(struct ptp_clock_info *ptp,
107 : : const struct timespec64 *ts)
108 : : {
109 : 0 : return -EOPNOTSUPP;
110 : : }
111 : :
112 : 0 : static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
113 : : {
114 : 0 : unsigned long ret;
115 : 0 : struct timespec64 tspec;
116 : :
117 : 0 : spin_lock(&kvm_ptp_lock);
118 : :
119 : 0 : ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
120 : : clock_pair_gpa,
121 : : KVM_CLOCK_PAIRING_WALLCLOCK);
122 [ # # ]: 0 : if (ret != 0) {
123 [ # # ]: 0 : pr_err_ratelimited("clock offset hypercall ret %lu\n", ret);
124 : 0 : spin_unlock(&kvm_ptp_lock);
125 : 0 : return -EOPNOTSUPP;
126 : : }
127 : :
128 : 0 : tspec.tv_sec = clock_pair.sec;
129 : 0 : tspec.tv_nsec = clock_pair.nsec;
130 : 0 : spin_unlock(&kvm_ptp_lock);
131 : :
132 : 0 : memcpy(ts, &tspec, sizeof(struct timespec64));
133 : :
134 : 0 : return 0;
135 : : }
136 : :
137 : 0 : static int ptp_kvm_enable(struct ptp_clock_info *ptp,
138 : : struct ptp_clock_request *rq, int on)
139 : : {
140 : 0 : return -EOPNOTSUPP;
141 : : }
142 : :
143 : : static 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 : :
160 : : static struct kvm_ptp_clock kvm_ptp_clock;
161 : :
162 : 0 : static void __exit ptp_kvm_exit(void)
163 : : {
164 : 0 : ptp_clock_unregister(kvm_ptp_clock.ptp_clock);
165 : 0 : }
166 : :
167 : 3 : static int __init ptp_kvm_init(void)
168 : : {
169 : 3 : long ret;
170 : :
171 [ + - ]: 3 : if (!kvm_para_available())
172 : : return -ENODEV;
173 : :
174 : 3 : clock_pair_gpa = slow_virt_to_phys(&clock_pair);
175 : 3 : hv_clock = pvclock_get_pvti_cpu0_va();
176 : :
177 [ + - ]: 3 : if (!hv_clock)
178 : : return -ENODEV;
179 : :
180 : 3 : ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa,
181 : : KVM_CLOCK_PAIRING_WALLCLOCK);
182 [ + - ]: 3 : if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP)
183 : : return -ENODEV;
184 : :
185 : 3 : kvm_ptp_clock.caps = ptp_kvm_caps;
186 : :
187 : 3 : kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL);
188 : :
189 [ - + ]: 3 : return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock);
190 : : }
191 : :
192 : : module_init(ptp_kvm_init);
193 : : module_exit(ptp_kvm_exit);
194 : :
195 : : MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
196 : : MODULE_DESCRIPTION("PTP clock using KVMCLOCK");
197 : : MODULE_LICENSE("GPL");
|