Skip to content

Commit 9a17125

Browse files
quic-philbermstsirkin
authored andcommitted
virtio_rtc: Add PTP clocks
Expose the virtio_rtc clocks as PTP clocks to userspace, similar to ptp_kvm. virtio_rtc can expose multiple clocks, e.g. a UTC clock and a monotonic clock. Userspace should distinguish different clocks through the name assigned by the driver. In particular, UTC-like clocks can also be distinguished by if and how leap seconds are smeared. udev rules such as the following can be used to get different symlinks for different clock types: SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 0/variant 0", SYMLINK += "ptp_virtio" SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 1/variant 0", SYMLINK += "ptp_virtio_tai" SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 2/variant 0", SYMLINK += "ptp_virtio_monotonic" SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 3/variant 0", SYMLINK += "ptp_virtio_smear_unspecified" SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 3/variant 1", SYMLINK += "ptp_virtio_smear_noon_linear" SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 3/variant 2", SYMLINK += "ptp_virtio_smear_sls" SUBSYSTEM=="ptp", ATTR{clock_name}=="Virtio PTP type 4/variant 0", SYMLINK += "ptp_virtio_maybe_smeared" The preferred PTP clock reading method is ioctl PTP_SYS_OFFSET_PRECISE2, through the ptp_clock_info.getcrosststamp() op. For now, PTP_SYS_OFFSET_PRECISE2 will return -EOPNOTSUPP through a weak function. PTP_SYS_OFFSET_PRECISE2 requires cross-timestamping support for specific clocksources, which will be added in the following. If the clocksource specific code is enabled, check that the Virtio RTC device supports the respective HW counter before obtaining an actual cross-timestamp from the Virtio device. The Virtio RTC device response time may be higher than the timekeeper seqcount increment interval. Therefore, obtain the cross-timestamp before calling get_device_system_crosststamp(). As a fallback, support the ioctl PTP_SYS_OFFSET_EXTENDED2 for all platforms. Assume that concurrency issues during PTP clock removal are avoided by the posix_clock framework. Kconfig recursive dependencies prevent virtio_rtc from implicitly enabling PTP_1588_CLOCK, therefore just warn the user if PTP_1588_CLOCK is not available. Since virtio_rtc should in the future also expose clocks as RTC class devices, do not depend VIRTIO_RTC on PTP_1588_CLOCK. Signed-off-by: Peter Hilber <quic_philber@quicinc.com> Message-Id: <20250509160734.1772-3-quic_philber@quicinc.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
1 parent 0623c75 commit 9a17125

File tree

5 files changed

+535
-4
lines changed

5 files changed

+535
-4
lines changed

drivers/virtio/Kconfig

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,33 @@ config VIRTIO_RTC
194194
depends on PTP_1588_CLOCK_OPTIONAL
195195
help
196196
This driver provides current time from a Virtio RTC device. The driver
197-
provides the time through one or more clocks.
197+
provides the time through one or more clocks. The Virtio RTC PTP
198+
clocks must be enabled to expose the clocks to userspace.
198199

199200
To compile this code as a module, choose M here: the module will be
200201
called virtio_rtc.
201202

202203
If unsure, say M.
203204

205+
if VIRTIO_RTC
206+
207+
comment "WARNING: Consider enabling VIRTIO_RTC_PTP."
208+
depends on !VIRTIO_RTC_PTP
209+
210+
comment "Enable PTP_1588_CLOCK in order to enable VIRTIO_RTC_PTP."
211+
depends on PTP_1588_CLOCK=n
212+
213+
config VIRTIO_RTC_PTP
214+
bool "Virtio RTC PTP clocks"
215+
default y
216+
depends on PTP_1588_CLOCK
217+
help
218+
This exposes any Virtio RTC clocks as PTP Hardware Clocks (PHCs) to
219+
userspace. The PHC sysfs attribute "clock_name" describes the clock
220+
type.
221+
222+
If unsure, say Y.
223+
224+
endif # VIRTIO_RTC
225+
204226
endif # VIRTIO_MENU

drivers/virtio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ obj-$(CONFIG_VIRTIO_DMA_SHARED_BUFFER) += virtio_dma_buf.o
1616
obj-$(CONFIG_VIRTIO_DEBUG) += virtio_debug.o
1717
obj-$(CONFIG_VIRTIO_RTC) += virtio_rtc.o
1818
virtio_rtc-y := virtio_rtc_driver.o
19+
virtio_rtc-$(CONFIG_VIRTIO_RTC_PTP) += virtio_rtc_ptp.o

drivers/virtio/virtio_rtc_driver.c

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,16 @@ struct viortc_vq {
3838
* struct viortc_dev - virtio_rtc device data
3939
* @vdev: virtio device
4040
* @vqs: virtqueues
41+
* @clocks_to_unregister: Clock references, which are only used during device
42+
* removal.
43+
* For other uses, there would be a race between device
44+
* creation and setting the pointers here.
4145
* @num_clocks: # of virtio_rtc clocks
4246
*/
4347
struct viortc_dev {
4448
struct virtio_device *vdev;
4549
struct viortc_vq vqs[VIORTC_MAX_NR_QUEUES];
50+
struct viortc_ptp_clock **clocks_to_unregister;
4651
u16 num_clocks;
4752
};
4853

@@ -638,6 +643,99 @@ int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_counter,
638643
* init, deinit
639644
*/
640645

646+
/**
647+
* viortc_init_ptp_clock() - init and register PTP clock
648+
* @viortc: device data
649+
* @vio_clk_id: virtio_rtc clock id
650+
* @clock_type: virtio_rtc clock type
651+
* @leap_second_smearing: virtio_rtc leap second smearing
652+
*
653+
* Context: Process context.
654+
* Return: Positive if registered, zero if not supported by configuration,
655+
* negative error code otherwise.
656+
*/
657+
static int viortc_init_ptp_clock(struct viortc_dev *viortc, u16 vio_clk_id,
658+
u8 clock_type, u8 leap_second_smearing)
659+
{
660+
struct device *dev = &viortc->vdev->dev;
661+
char ptp_clock_name[PTP_CLOCK_NAME_LEN];
662+
struct viortc_ptp_clock *vio_ptp;
663+
664+
snprintf(ptp_clock_name, PTP_CLOCK_NAME_LEN,
665+
"Virtio PTP type %hhu/variant %hhu", clock_type,
666+
leap_second_smearing);
667+
668+
vio_ptp = viortc_ptp_register(viortc, dev, vio_clk_id, ptp_clock_name);
669+
if (IS_ERR(vio_ptp)) {
670+
dev_err(dev, "failed to register PTP clock '%s'\n",
671+
ptp_clock_name);
672+
return PTR_ERR(vio_ptp);
673+
}
674+
675+
viortc->clocks_to_unregister[vio_clk_id] = vio_ptp;
676+
677+
return !!vio_ptp;
678+
}
679+
680+
/**
681+
* viortc_init_clock() - init local representation of virtio_rtc clock
682+
* @viortc: device data
683+
* @vio_clk_id: virtio_rtc clock id
684+
*
685+
* Initializes PHC to represent virtio_rtc clock.
686+
*
687+
* Context: Process context.
688+
* Return: Zero on success, negative error code otherwise.
689+
*/
690+
static int viortc_init_clock(struct viortc_dev *viortc, u16 vio_clk_id)
691+
{
692+
u8 clock_type, leap_second_smearing;
693+
bool is_exposed = false;
694+
int ret;
695+
696+
ret = viortc_clock_cap(viortc, vio_clk_id, &clock_type,
697+
&leap_second_smearing);
698+
if (ret)
699+
return ret;
700+
701+
if (IS_ENABLED(CONFIG_VIRTIO_RTC_PTP)) {
702+
ret = viortc_init_ptp_clock(viortc, vio_clk_id, clock_type,
703+
leap_second_smearing);
704+
if (ret < 0)
705+
return ret;
706+
if (ret > 0)
707+
is_exposed = true;
708+
}
709+
710+
if (!is_exposed)
711+
dev_warn(&viortc->vdev->dev,
712+
"cannot expose clock %d (type %d, variant %d) to userspace\n",
713+
vio_clk_id, clock_type, leap_second_smearing);
714+
715+
return 0;
716+
}
717+
718+
/**
719+
* viortc_clocks_deinit() - unregister PHCs
720+
* @viortc: device data
721+
*/
722+
static void viortc_clocks_deinit(struct viortc_dev *viortc)
723+
{
724+
struct viortc_ptp_clock *vio_ptp;
725+
unsigned int i;
726+
727+
for (i = 0; i < viortc->num_clocks; i++) {
728+
vio_ptp = viortc->clocks_to_unregister[i];
729+
730+
if (!vio_ptp)
731+
continue;
732+
733+
viortc->clocks_to_unregister[i] = NULL;
734+
735+
WARN_ON(viortc_ptp_unregister(vio_ptp, &viortc->vdev->dev));
736+
}
737+
}
738+
641739
/**
642740
* viortc_clocks_init() - init local representations of virtio_rtc clocks
643741
* @viortc: device data
@@ -648,6 +746,7 @@ int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_counter,
648746
static int viortc_clocks_init(struct viortc_dev *viortc)
649747
{
650748
u16 num_clocks;
749+
unsigned int i;
651750
int ret;
652751

653752
ret = viortc_cfg(viortc, &num_clocks);
@@ -661,8 +760,22 @@ static int viortc_clocks_init(struct viortc_dev *viortc)
661760

662761
viortc->num_clocks = num_clocks;
663762

664-
/* In the future, PTP clocks will be initialized here. */
665-
(void)viortc_clock_cap;
763+
viortc->clocks_to_unregister =
764+
devm_kcalloc(&viortc->vdev->dev, num_clocks,
765+
sizeof(*viortc->clocks_to_unregister), GFP_KERNEL);
766+
if (!viortc->clocks_to_unregister)
767+
return -ENOMEM;
768+
769+
for (i = 0; i < num_clocks; i++) {
770+
ret = viortc_init_clock(viortc, i);
771+
if (ret)
772+
goto err_deinit_clocks;
773+
}
774+
775+
return 0;
776+
777+
err_deinit_clocks:
778+
viortc_clocks_deinit(viortc);
666779

667780
return ret;
668781
}
@@ -741,7 +854,9 @@ static int viortc_probe(struct virtio_device *vdev)
741854
*/
742855
static void viortc_remove(struct virtio_device *vdev)
743856
{
744-
/* In the future, PTP clocks will be deinitialized here. */
857+
struct viortc_dev *viortc = vdev->priv;
858+
859+
viortc_clocks_deinit(viortc);
745860

746861
virtio_reset_device(vdev);
747862
vdev->config->del_vqs(vdev);

drivers/virtio/virtio_rtc_internal.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef _VIRTIO_RTC_INTERNAL_H_
1010
#define _VIRTIO_RTC_INTERNAL_H_
1111

12+
#include <linux/ptp_clock_kernel.h>
1213
#include <linux/types.h>
1314

1415
/* driver core IFs */
@@ -21,4 +22,49 @@ int viortc_read_cross(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_counter,
2122
int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_counter,
2223
bool *supported);
2324

25+
/* PTP IFs */
26+
27+
struct viortc_ptp_clock;
28+
29+
#if IS_ENABLED(CONFIG_VIRTIO_RTC_PTP)
30+
31+
struct viortc_ptp_clock *viortc_ptp_register(struct viortc_dev *viortc,
32+
struct device *parent_dev,
33+
u16 vio_clk_id,
34+
const char *ptp_clock_name);
35+
int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp,
36+
struct device *parent_dev);
37+
38+
#else
39+
40+
static inline struct viortc_ptp_clock *
41+
viortc_ptp_register(struct viortc_dev *viortc, struct device *parent_dev,
42+
u16 vio_clk_id, const char *ptp_clock_name)
43+
{
44+
return NULL;
45+
}
46+
47+
static inline int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp,
48+
struct device *parent_dev)
49+
{
50+
return -ENODEV;
51+
}
52+
53+
#endif
54+
55+
/* HW counter IFs */
56+
57+
/**
58+
* viortc_hw_xtstamp_params() - get HW-specific xtstamp params
59+
* @hw_counter: virtio_rtc HW counter type
60+
* @cs_id: clocksource id corresponding to hw_counter
61+
*
62+
* Gets the HW-specific xtstamp params. Returns an error if the driver cannot
63+
* support xtstamp.
64+
*
65+
* Context: Process context.
66+
* Return: Zero on success, negative error code otherwise.
67+
*/
68+
int viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *cs_id);
69+
2470
#endif /* _VIRTIO_RTC_INTERNAL_H_ */

0 commit comments

Comments
 (0)