ptp_kvm_common.c (3192B)
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/slab.h> 12#include <linux/module.h> 13#include <linux/ptp_kvm.h> 14#include <uapi/linux/kvm_para.h> 15#include <asm/kvm_para.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 int ptp_kvm_get_time_fn(ktime_t *device_time, 28 struct system_counterval_t *system_counter, 29 void *ctx) 30{ 31 long ret; 32 u64 cycle; 33 struct timespec64 tspec; 34 struct clocksource *cs; 35 36 spin_lock(&kvm_ptp_lock); 37 38 preempt_disable_notrace(); 39 ret = kvm_arch_ptp_get_crosststamp(&cycle, &tspec, &cs); 40 if (ret) { 41 spin_unlock(&kvm_ptp_lock); 42 preempt_enable_notrace(); 43 return ret; 44 } 45 46 preempt_enable_notrace(); 47 48 system_counter->cycles = cycle; 49 system_counter->cs = cs; 50 51 *device_time = timespec64_to_ktime(tspec); 52 53 spin_unlock(&kvm_ptp_lock); 54 55 return 0; 56} 57 58static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp, 59 struct system_device_crosststamp *xtstamp) 60{ 61 return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL, 62 NULL, xtstamp); 63} 64 65/* 66 * PTP clock operations 67 */ 68 69static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 70{ 71 return -EOPNOTSUPP; 72} 73 74static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta) 75{ 76 return -EOPNOTSUPP; 77} 78 79static int ptp_kvm_settime(struct ptp_clock_info *ptp, 80 const struct timespec64 *ts) 81{ 82 return -EOPNOTSUPP; 83} 84 85static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 86{ 87 long ret; 88 struct timespec64 tspec; 89 90 spin_lock(&kvm_ptp_lock); 91 92 ret = kvm_arch_ptp_get_clock(&tspec); 93 if (ret) { 94 spin_unlock(&kvm_ptp_lock); 95 return ret; 96 } 97 98 spin_unlock(&kvm_ptp_lock); 99 100 memcpy(ts, &tspec, sizeof(struct timespec64)); 101 102 return 0; 103} 104 105static int ptp_kvm_enable(struct ptp_clock_info *ptp, 106 struct ptp_clock_request *rq, int on) 107{ 108 return -EOPNOTSUPP; 109} 110 111static const struct ptp_clock_info ptp_kvm_caps = { 112 .owner = THIS_MODULE, 113 .name = "KVM virtual PTP", 114 .max_adj = 0, 115 .n_ext_ts = 0, 116 .n_pins = 0, 117 .pps = 0, 118 .adjfreq = ptp_kvm_adjfreq, 119 .adjtime = ptp_kvm_adjtime, 120 .gettime64 = ptp_kvm_gettime, 121 .settime64 = ptp_kvm_settime, 122 .enable = ptp_kvm_enable, 123 .getcrosststamp = ptp_kvm_getcrosststamp, 124}; 125 126/* module operations */ 127 128static struct kvm_ptp_clock kvm_ptp_clock; 129 130static void __exit ptp_kvm_exit(void) 131{ 132 ptp_clock_unregister(kvm_ptp_clock.ptp_clock); 133} 134 135static int __init ptp_kvm_init(void) 136{ 137 long ret; 138 139 ret = kvm_arch_ptp_init(); 140 if (ret) { 141 if (ret != -EOPNOTSUPP) 142 pr_err("fail to initialize ptp_kvm"); 143 return ret; 144 } 145 146 kvm_ptp_clock.caps = ptp_kvm_caps; 147 148 kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); 149 150 return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock); 151} 152 153module_init(ptp_kvm_init); 154module_exit(ptp_kvm_exit); 155 156MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>"); 157MODULE_DESCRIPTION("PTP clock using KVMCLOCK"); 158MODULE_LICENSE("GPL");