From patchwork Fri Jan 13 12:01:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcelo Tosatti X-Patchwork-Id: 9515445 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A3A7C6077E for ; Fri, 13 Jan 2017 12:09:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 992D2285F4 for ; Fri, 13 Jan 2017 12:09:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8D61F2862E; Fri, 13 Jan 2017 12:09:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 140D5285F4 for ; Fri, 13 Jan 2017 12:09:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752516AbdAMMJ0 (ORCPT ); Fri, 13 Jan 2017 07:09:26 -0500 Received: from mx1.redhat.com ([209.132.183.28]:44096 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752249AbdAMMEC (ORCPT ); Fri, 13 Jan 2017 07:04:02 -0500 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 56763C04D2EC; Fri, 13 Jan 2017 12:03:53 +0000 (UTC) Received: from amt.cnet (vpn1-4-149.gru2.redhat.com [10.97.4.149]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v0DC3q7n021536; Fri, 13 Jan 2017 07:03:52 -0500 Received: from amt.cnet (localhost [127.0.0.1]) by amt.cnet (Postfix) with ESMTP id A2D011008C8; Fri, 13 Jan 2017 10:03:37 -0200 (BRST) Received: (from marcelo@localhost) by amt.cnet (8.14.7/8.14.7/Submit) id v0DC3biJ030301; Fri, 13 Jan 2017 10:03:37 -0200 Message-Id: <20170113120319.777765254@redhat.com> User-Agent: quilt/0.60-1 Date: Fri, 13 Jan 2017 10:01:34 -0200 From: Marcelo Tosatti To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Paolo Bonzini , Radim Krcmar , Richard Cochran , Miroslav Lichvar Subject: [patch 3/3] PTP: add kvm PTP driver References: <20170113120131.086634482@redhat.com> Content-Disposition: inline; filename=kvm-ptpdriver X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Fri, 13 Jan 2017 12:03:53 +0000 (UTC) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a driver with gettime method returning hosts realtime clock. This allows Chrony to synchronize host and guest clocks with high precision (see results below). chronyc> sources MS Name/IP address Stratum Poll Reach LastRx Last sample --- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html =============================================================================== #* PHC0 0 3 377 6 +4ns[ +4ns] +/- 3ns To configure Chronyd to use PHC refclock, add the following line to its configuration file: refclock PHC /dev/ptpX poll 3 dpoll -2 offset 0 Where /dev/ptpX is the kvmclock PTP clock. --- drivers/ptp/Kconfig | 12 +++ drivers/ptp/Makefile | 1 drivers/ptp/ptp_kvm.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) Index: kvm-ptpdriver/drivers/ptp/Kconfig =================================================================== --- kvm-ptpdriver.orig/drivers/ptp/Kconfig 2017-01-13 09:17:31.724568567 -0200 +++ kvm-ptpdriver/drivers/ptp/Kconfig 2017-01-13 09:55:33.344208894 -0200 @@ -90,4 +90,16 @@ To compile this driver as a module, choose M here: the module will be called ptp_pch. +config PTP_1588_CLOCK_KVM + tristate "KVM virtual PTP clock" + depends on PTP_1588_CLOCK + depends on KVM_GUEST + default y + help + This driver adds support for using kvm infrastructure as a PTP + clock. This clock is only useful if you are using KVM guests. + + To compile this driver as a module, choose M here: the module + will be called ptp_kvm. + endmenu Index: kvm-ptpdriver/drivers/ptp/ptp_kvm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ kvm-ptpdriver/drivers/ptp/ptp_kvm.c 2017-01-13 09:57:55.013440645 -0200 @@ -0,0 +1,180 @@ +/* + * Virtual PTP 1588 clock for use with KVM guests + * + * Copyright (C) 2017 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct kvm_ptp_clock { + struct ptp_clock *ptp_clock; + struct ptp_clock_info caps; +}; + +DEFINE_SPINLOCK(kvm_ptp_lock); + +static struct pvclock_vsyscall_time_info *hv_clock; + +/* + * PTP clock operations + */ + +static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +{ + return -EOPNOTSUPP; +} + +static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + return -EOPNOTSUPP; +} + +static struct kvm_clock_offset clock_off; +static phys_addr_t clock_off_gpa; + +static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + unsigned long ret; + struct timespec64 tspec; + u64 delta; + cycle_t offset; + unsigned version; + int cpu; + struct pvclock_vcpu_time_info *src; + + preempt_disable_notrace(); + cpu = smp_processor_id(); + src = &hv_clock[cpu].pvti; + + spin_lock(&kvm_ptp_lock); + + do { + /* + * We are measuring the delay between + * kvm_hypercall and rdtsc using TSC, + * and converting that delta to + * tsc_to_system_mul and tsc_shift + * So any changes to tsc_to_system_mul + * and tsc_shift in this region + * invalidate the measurement. + */ + version = pvclock_read_begin(src); + + ret = kvm_hypercall2(KVM_HC_CLOCK_OFFSET, + clock_off_gpa, + KVM_CLOCK_OFFSET_WALLCLOCK); + if (ret != 0) { + pr_err("clock offset hypercall ret %lu\n", ret); + spin_unlock(&kvm_ptp_lock); + preempt_enable_notrace(); + return -EOPNOTSUPP; + } + + tspec.tv_sec = clock_off.sec; + tspec.tv_nsec = clock_off.nsec; + + delta = rdtsc_ordered() - clock_off.tsc; + + offset = pvclock_scale_delta(delta, src->tsc_to_system_mul, + src->tsc_shift); + + } while (pvclock_read_retry(src, version)); + + preempt_enable_notrace(); + + tspec.tv_nsec = tspec.tv_nsec + offset; + + spin_unlock(&kvm_ptp_lock); + + if (tspec.tv_nsec >= NSEC_PER_SEC) { + u64 secs = tspec.tv_nsec; + + tspec.tv_nsec = do_div(secs, NSEC_PER_SEC); + tspec.tv_sec += secs; + } + + memcpy(ts, &tspec, sizeof(struct timespec64)); + + return 0; +} + +static int ptp_kvm_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + return -EOPNOTSUPP; +} + +static int ptp_kvm_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +static struct ptp_clock_info ptp_kvm_caps = { + .owner = THIS_MODULE, + .name = "KVM virtual PTP", + .max_adj = 0, + .n_ext_ts = 0, + .n_pins = 0, + .pps = 0, + .adjfreq = ptp_kvm_adjfreq, + .adjtime = ptp_kvm_adjtime, + .gettime64 = ptp_kvm_gettime, + .settime64 = ptp_kvm_settime, + .enable = ptp_kvm_enable, +}; + +/* module operations */ + +static struct kvm_ptp_clock kvm_ptp_clock; + +static void __exit ptp_kvm_exit(void) +{ + ptp_clock_unregister(kvm_ptp_clock.ptp_clock); +} + +static int __init ptp_kvm_init(void) +{ + if (!kvm_para_available()) + return -ENODEV; + + kvm_ptp_clock.caps = ptp_kvm_caps; + + kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); + + if (IS_ERR(kvm_ptp_clock.ptp_clock)) + return PTR_ERR(kvm_ptp_clock.ptp_clock); + + clock_off_gpa = slow_virt_to_phys(&clock_off); + + hv_clock = pvclock_pvti_cpu0_va(); + + return 0; +} + +module_init(ptp_kvm_init); +module_exit(ptp_kvm_exit); + +MODULE_AUTHOR("Marcelo Tosatti "); +MODULE_DESCRIPTION("PTP clock using KVMCLOCK"); +MODULE_LICENSE("GPL"); Index: kvm-ptpdriver/drivers/ptp/Makefile =================================================================== --- kvm-ptpdriver.orig/drivers/ptp/Makefile 2017-01-13 09:17:31.724568567 -0200 +++ kvm-ptpdriver/drivers/ptp/Makefile 2017-01-13 09:17:58.997609570 -0200 @@ -6,3 +6,4 @@ obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o +obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o