root/drivers/ptp/ptp_kvm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ptp_kvm_get_time_fn
  2. ptp_kvm_getcrosststamp
  3. ptp_kvm_adjfreq
  4. ptp_kvm_adjtime
  5. ptp_kvm_settime
  6. ptp_kvm_gettime
  7. ptp_kvm_enable
  8. ptp_kvm_exit
  9. ptp_kvm_init

   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 static 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 
  85 static 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 
  96 static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
  97 {
  98         return -EOPNOTSUPP;
  99 }
 100 
 101 static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta)
 102 {
 103         return -EOPNOTSUPP;
 104 }
 105 
 106 static int ptp_kvm_settime(struct ptp_clock_info *ptp,
 107                            const struct timespec64 *ts)
 108 {
 109         return -EOPNOTSUPP;
 110 }
 111 
 112 static 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 
 137 static int ptp_kvm_enable(struct ptp_clock_info *ptp,
 138                           struct ptp_clock_request *rq, int on)
 139 {
 140         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 static void __exit ptp_kvm_exit(void)
 163 {
 164         ptp_clock_unregister(kvm_ptp_clock.ptp_clock);
 165 }
 166 
 167 static 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 
 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");

/* [<][>][^][v][top][bottom][index][help] */