ptp_kvm.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Virtual PTP 1588 clock for use with KVM guests
  3. *
  4. * Copyright (C) 2017 Red Hat Inc.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. */
  17. #include <linux/device.h>
  18. #include <linux/err.h>
  19. #include <linux/init.h>
  20. #include <linux/kernel.h>
  21. #include <linux/module.h>
  22. #include <uapi/linux/kvm_para.h>
  23. #include <asm/kvm_para.h>
  24. #include <asm/pvclock.h>
  25. #include <asm/kvmclock.h>
  26. #include <uapi/asm/kvm_para.h>
  27. #include <linux/ptp_clock_kernel.h>
  28. struct kvm_ptp_clock {
  29. struct ptp_clock *ptp_clock;
  30. struct ptp_clock_info caps;
  31. };
  32. DEFINE_SPINLOCK(kvm_ptp_lock);
  33. static struct pvclock_vsyscall_time_info *hv_clock;
  34. static struct kvm_clock_pairing clock_pair;
  35. static phys_addr_t clock_pair_gpa;
  36. static int ptp_kvm_get_time_fn(ktime_t *device_time,
  37. struct system_counterval_t *system_counter,
  38. void *ctx)
  39. {
  40. unsigned long ret;
  41. struct timespec64 tspec;
  42. unsigned version;
  43. int cpu;
  44. struct pvclock_vcpu_time_info *src;
  45. spin_lock(&kvm_ptp_lock);
  46. preempt_disable_notrace();
  47. cpu = smp_processor_id();
  48. src = &hv_clock[cpu].pvti;
  49. do {
  50. /*
  51. * We are using a TSC value read in the hosts
  52. * kvm_hc_clock_pairing handling.
  53. * So any changes to tsc_to_system_mul
  54. * and tsc_shift or any other pvclock
  55. * data invalidate that measurement.
  56. */
  57. version = pvclock_read_begin(src);
  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. tspec.tv_sec = clock_pair.sec;
  68. tspec.tv_nsec = clock_pair.nsec;
  69. ret = __pvclock_read_cycles(src, clock_pair.tsc);
  70. } while (pvclock_read_retry(src, version));
  71. preempt_enable_notrace();
  72. system_counter->cycles = ret;
  73. system_counter->cs = &kvm_clock;
  74. *device_time = timespec64_to_ktime(tspec);
  75. spin_unlock(&kvm_ptp_lock);
  76. return 0;
  77. }
  78. static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp,
  79. struct system_device_crosststamp *xtstamp)
  80. {
  81. return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL,
  82. NULL, xtstamp);
  83. }
  84. /*
  85. * PTP clock operations
  86. */
  87. static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
  88. {
  89. return -EOPNOTSUPP;
  90. }
  91. static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta)
  92. {
  93. return -EOPNOTSUPP;
  94. }
  95. static int ptp_kvm_settime(struct ptp_clock_info *ptp,
  96. const struct timespec64 *ts)
  97. {
  98. return -EOPNOTSUPP;
  99. }
  100. static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
  101. {
  102. unsigned long ret;
  103. struct timespec64 tspec;
  104. spin_lock(&kvm_ptp_lock);
  105. ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
  106. clock_pair_gpa,
  107. KVM_CLOCK_PAIRING_WALLCLOCK);
  108. if (ret != 0) {
  109. pr_err_ratelimited("clock offset hypercall ret %lu\n", ret);
  110. spin_unlock(&kvm_ptp_lock);
  111. return -EOPNOTSUPP;
  112. }
  113. tspec.tv_sec = clock_pair.sec;
  114. tspec.tv_nsec = clock_pair.nsec;
  115. spin_unlock(&kvm_ptp_lock);
  116. memcpy(ts, &tspec, sizeof(struct timespec64));
  117. return 0;
  118. }
  119. static int ptp_kvm_enable(struct ptp_clock_info *ptp,
  120. struct ptp_clock_request *rq, int on)
  121. {
  122. return -EOPNOTSUPP;
  123. }
  124. static const struct ptp_clock_info ptp_kvm_caps = {
  125. .owner = THIS_MODULE,
  126. .name = "KVM virtual PTP",
  127. .max_adj = 0,
  128. .n_ext_ts = 0,
  129. .n_pins = 0,
  130. .pps = 0,
  131. .adjfreq = ptp_kvm_adjfreq,
  132. .adjtime = ptp_kvm_adjtime,
  133. .gettime64 = ptp_kvm_gettime,
  134. .settime64 = ptp_kvm_settime,
  135. .enable = ptp_kvm_enable,
  136. .getcrosststamp = ptp_kvm_getcrosststamp,
  137. };
  138. /* module operations */
  139. static struct kvm_ptp_clock kvm_ptp_clock;
  140. static void __exit ptp_kvm_exit(void)
  141. {
  142. ptp_clock_unregister(kvm_ptp_clock.ptp_clock);
  143. }
  144. static int __init ptp_kvm_init(void)
  145. {
  146. long ret;
  147. if (!kvm_para_available())
  148. return -ENODEV;
  149. clock_pair_gpa = slow_virt_to_phys(&clock_pair);
  150. hv_clock = pvclock_get_pvti_cpu0_va();
  151. if (!hv_clock)
  152. return -ENODEV;
  153. ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa,
  154. KVM_CLOCK_PAIRING_WALLCLOCK);
  155. if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP)
  156. return -ENODEV;
  157. kvm_ptp_clock.caps = ptp_kvm_caps;
  158. kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL);
  159. return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock);
  160. }
  161. module_init(ptp_kvm_init);
  162. module_exit(ptp_kvm_exit);
  163. MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
  164. MODULE_DESCRIPTION("PTP clock using KVMCLOCK");
  165. MODULE_LICENSE("GPL");