From: Arthur Kiyanovski <akiyano@amazon.com>
To: David Miller <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>, <netdev@vger.kernel.org>
Cc: Arthur Kiyanovski <akiyano@amazon.com>,
Richard Cochran <richardcochran@gmail.com>,
Eric Dumazet <edumazet@google.com>,
Paolo Abeni <pabeni@redhat.com>,
David Woodhouse <dwmw2@infradead.org>,
Thomas Gleixner <tglx@linutronix.de>,
Miroslav Lichvar <mlichvar@redhat.com>,
Andrew Lunn <andrew+netdev@lunn.ch>,
Wen Gu <guwen@linux.alibaba.com>,
Xuan Zhuo <xuanzhuo@linux.alibaba.com>,
David Woodhouse <dwmw@amazon.com>,
"Yonatan Sarna" <ysarna@amazon.com>,
Zorik Machulsky <zorik@amazon.com>,
"Alexander Matushevsky" <matua@amazon.com>,
Saeed Bshara <saeedb@amazon.com>, Matt Wilson <msw@amazon.com>,
Anthony Liguori <aliguori@amazon.com>,
Nafea Bshara <nafea@amazon.com>,
Evgeny Schmeilin <evgenys@amazon.com>,
Netanel Belgazal <netanel@amazon.com>,
Ali Saidi <alisaidi@amazon.com>,
Benjamin Herrenschmidt <benh@amazon.com>,
Noam Dagan <ndagan@amazon.com>,
David Arinzon <darinzon@amazon.com>,
Evgeny Ostrovsky <evostrov@amazon.com>,
Ofir Tabachnik <ofirt@amazon.com>,
Amit Bernstein <amitbern@amazon.com>,
<linux-kselftest@vger.kernel.org>, <shuah@kernel.org>,
<vadim.fedorenko@linux.dev>
Subject: [PATCH v2 net-next 4/8] ptp: ptp_vmclock: Implement attributes ioctls
Date: Thu, 30 Apr 2026 03:25:01 +0000 [thread overview]
Message-ID: <20260430032507.11586-5-akiyano@amazon.com> (raw)
In-Reply-To: <20260430032507.11586-1-akiyano@amazon.com>
Implement the gettimexattrs64 and getcrosststampattrs callbacks in the
ptp_vmclock driver to provide clock quality attributes through the new
PTP_SYS_OFFSET_EXTENDED_ATTRS and PTP_SYS_OFFSET_PRECISE_ATTRS ioctls.
The ptp_vmclock device exposes:
- error_bound: Derived from time_maxerror_nanosec, accumulated with
counter frequency error (counter_period_maxerror_rate_frac_sec) over
elapsed counter ticks
- clock_status: Mapped from the device's clock_status field
- timescale: Determined from time_type (UTC, TAI, monotonic, etc.)
The legacy ioctls return -EINVAL when clock_status is UNRELIABLE since
they have no way to communicate clock state to userspace. The attrs
ioctls have a status field for this purpose, so they treat UNRELIABLE
as success and let userspace check the status field.
To avoid a race where the hypervisor could update clock_status between
the timestamp call and the UNRELIABLE check, the clock state is captured
inside the seq_count loop for a consistent snapshot with the timestamp.
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
drivers/ptp/ptp_vmclock.c | 195 ++++++++++++++++++++++++++++++++++----
1 file changed, 179 insertions(+), 16 deletions(-)
diff --git a/drivers/ptp/ptp_vmclock.c b/drivers/ptp/ptp_vmclock.c
index 8b630eb..5657c06 100644
--- a/drivers/ptp/ptp_vmclock.c
+++ b/drivers/ptp/ptp_vmclock.c
@@ -53,6 +53,17 @@ struct vmclock_state {
char *name;
};
+/**
+ * struct vmclock_crosststamp_ctx - context for get_device_system_crosststamp()
+ * @st: vmclock device state
+ * @attrs: optional output for PTP clock attributes, populated inside the
+ * seq_count loop for a consistent snapshot with the timestamp
+ */
+struct vmclock_crosststamp_ctx {
+ struct vmclock_state *st;
+ struct ptp_clock_attributes *attrs;
+};
+
#define VMCLOCK_MAX_WAIT ms_to_ktime(100)
/* Require at least the flags field to be present. All else can be optional. */
@@ -95,14 +106,109 @@ static bool tai_adjust(struct vmclock_abi *clk, uint64_t *sec)
return false;
}
+static uint8_t vmclock_get_ptp_timescale(uint8_t vmclock_time_type)
+{
+ switch (vmclock_time_type) {
+ case VMCLOCK_TIME_UTC:
+ return PTP_TIMESCALE_UTC;
+ case VMCLOCK_TIME_TAI:
+ return PTP_TIMESCALE_TAI;
+ case VMCLOCK_TIME_MONOTONIC:
+ return PTP_TIMESCALE_MONOTONIC;
+ default:
+ return PTP_TIMESCALE_UNKNOWN;
+ }
+}
+
+static uint8_t vmclock_get_ptp_status(uint8_t vmclock_status)
+{
+ switch (vmclock_status) {
+ case VMCLOCK_STATUS_UNKNOWN:
+ return PTP_CLOCK_STATUS_UNKNOWN;
+ case VMCLOCK_STATUS_INITIALIZING:
+ return PTP_CLOCK_STATUS_INITIALIZING;
+ case VMCLOCK_STATUS_SYNCHRONIZED:
+ return PTP_CLOCK_STATUS_SYNCED;
+ case VMCLOCK_STATUS_FREERUNNING:
+ return PTP_CLOCK_STATUS_FREE_RUNNING;
+ case VMCLOCK_STATUS_UNRELIABLE:
+ return PTP_CLOCK_STATUS_UNRELIABLE;
+ default:
+ return PTP_CLOCK_STATUS_UNKNOWN;
+ }
+}
+
+static void vmclock_populate_ptp_attributes(struct vmclock_state *st,
+ struct ptp_clock_attributes *att,
+ uint64_t delta,
+ uint64_t cycle)
+{
+ uint64_t maxerror_ns = UINT_MAX;
+
+ if (!att)
+ return;
+
+ /* Only calculate if the base error is flagged as valid
+ * by the hypervisor.
+ */
+ if (VMCLOCK_FIELD_PRESENT(st->clk, time_maxerror_nanosec) &&
+ (le64_to_cpu(st->clk->flags) & VMCLOCK_FLAG_TIME_MAXERROR_VALID)) {
+ maxerror_ns = le64_to_cpu(st->clk->time_maxerror_nanosec);
+
+ /* If frequency error is also valid, accumulate it
+ * over the delta.
+ */
+ if (VMCLOCK_FIELD_PRESENT(st->clk, counter_period_maxerror_rate_frac_sec) &&
+ (le64_to_cpu(st->clk->flags) & VMCLOCK_FLAG_PERIOD_MAXERROR_VALID)) {
+ uint64_t maxerror_rate, err_hi, err_frac, growth_ns;
+
+ maxerror_rate = le64_to_cpu(st->clk->counter_period_maxerror_rate_frac_sec);
+ err_frac = mul_u64_u64_shr_add_u64(&err_hi, delta,
+ maxerror_rate,
+ st->clk->counter_period_shift,
+ 0);
+
+ growth_ns = (err_hi * NSEC_PER_SEC) +
+ mul_u64_u64_shr(err_frac, NSEC_PER_SEC, 64);
+
+ /* Guard against overflow */
+ if (U64_MAX - growth_ns < maxerror_ns)
+ maxerror_ns = U64_MAX;
+ else
+ maxerror_ns += growth_ns;
+ }
+ }
+
+ /* PTP UAPI error_bound is 32-bit nanoseconds */
+ att->error_bound = (maxerror_ns > UINT_MAX) ?
+ UINT_MAX : (uint32_t)maxerror_ns;
+ att->timescale = vmclock_get_ptp_timescale(st->clk->time_type);
+ att->status = vmclock_get_ptp_status(st->clk->clock_status);
+
+ att->counter_value = cycle;
+ switch (st->cs_id) {
+ case CSID_X86_TSC:
+ att->counter_id = PTP_COUNTER_X86_TSC;
+ break;
+ case CSID_ARM_ARCH_COUNTER:
+ att->counter_id = PTP_COUNTER_ARM_ARCH;
+ break;
+ default:
+ att->counter_id = PTP_COUNTER_UNKNOWN;
+ break;
+ }
+}
+
static int vmclock_get_crosststamp(struct vmclock_state *st,
struct ptp_system_timestamp *sts,
struct system_counterval_t *system_counter,
- struct timespec64 *tspec)
+ struct timespec64 *tspec,
+ struct ptp_clock_attributes *attrs)
{
ktime_t deadline = ktime_add(ktime_get(), VMCLOCK_MAX_WAIT);
struct system_time_snapshot systime_snapshot;
uint64_t cycle, delta, seq, frac_sec;
+ uint8_t clock_status = VMCLOCK_STATUS_UNKNOWN;
#ifdef CONFIG_X86
/*
@@ -122,9 +228,6 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
*/
virt_rmb();
- if (st->clk->clock_status == VMCLOCK_STATUS_UNRELIABLE)
- return -EINVAL;
-
/*
* When invoked for gettimex64(), fill in the pre/post system
* times. The simple case is when system time is based on the
@@ -163,6 +266,18 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
if (!tai_adjust(st->clk, &tspec->tv_sec))
return -EINVAL;
+ /*
+ * Capture clock state inside the seq_count loop for a
+ * consistent snapshot with the timestamp. The attrs path
+ * reports it to userspace via the status field; the legacy
+ * path saves it for the UNRELIABLE check after the loop.
+ */
+ if (attrs)
+ vmclock_populate_ptp_attributes(st, attrs, delta,
+ cycle);
+ else
+ clock_status = st->clk->clock_status;
+
/*
* This pairs with a write barrier in the hypervisor
* which populates this structure.
@@ -186,6 +301,17 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
sts->post_ts = sts->pre_ts;
}
+ /*
+ * If attrs is set, attributes were already populated inside the
+ * seq_count loop. Return success even for UNRELIABLE — the attrs
+ * ioctl can report the status to userspace.
+ */
+ if (attrs)
+ return 0;
+
+ if (clock_status == VMCLOCK_STATUS_UNRELIABLE)
+ return -EINVAL;
+
return 0;
}
@@ -198,7 +324,8 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
static int vmclock_get_crosststamp_kvmclock(struct vmclock_state *st,
struct ptp_system_timestamp *sts,
struct system_counterval_t *system_counter,
- struct timespec64 *tspec)
+ struct timespec64 *tspec,
+ struct ptp_clock_attributes *attrs)
{
struct pvclock_vcpu_time_info *pvti = this_cpu_pvti();
unsigned int pvti_ver;
@@ -209,7 +336,8 @@ static int vmclock_get_crosststamp_kvmclock(struct vmclock_state *st,
do {
pvti_ver = pvclock_read_begin(pvti);
- ret = vmclock_get_crosststamp(st, sts, system_counter, tspec);
+ ret = vmclock_get_crosststamp(st, sts, system_counter, tspec,
+ attrs);
if (ret)
break;
@@ -238,17 +366,19 @@ static int ptp_vmclock_get_time_fn(ktime_t *device_time,
struct system_counterval_t *system_counter,
void *ctx)
{
- struct vmclock_state *st = ctx;
+ struct vmclock_crosststamp_ctx *vctx = ctx;
+ struct vmclock_state *st = vctx->st;
struct timespec64 tspec;
int ret;
#ifdef SUPPORT_KVMCLOCK
if (READ_ONCE(st->sys_cs_id) == CSID_X86_KVM_CLK)
ret = vmclock_get_crosststamp_kvmclock(st, NULL, system_counter,
- &tspec);
+ &tspec, vctx->attrs);
else
#endif
- ret = vmclock_get_crosststamp(st, NULL, system_counter, &tspec);
+ ret = vmclock_get_crosststamp(st, NULL, system_counter, &tspec,
+ vctx->attrs);
if (!ret)
*device_time = timespec64_to_ktime(tspec);
@@ -256,12 +386,11 @@ static int ptp_vmclock_get_time_fn(ktime_t *device_time,
return ret;
}
-static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
- struct system_device_crosststamp *xtstamp)
+static int ptp_vmclock_do_getcrosststamp(struct vmclock_crosststamp_ctx *vctx,
+ struct system_device_crosststamp *xtstamp)
{
- struct vmclock_state *st = container_of(ptp, struct vmclock_state,
- ptp_clock_info);
- int ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn, st,
+ struct vmclock_state *st = vctx->st;
+ int ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn, vctx,
NULL, xtstamp);
#ifdef SUPPORT_KVMCLOCK
/*
@@ -278,13 +407,23 @@ static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
systime_snapshot.cs_id == CSID_X86_KVM_CLK) {
WRITE_ONCE(st->sys_cs_id, systime_snapshot.cs_id);
ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn,
- st, NULL, xtstamp);
+ vctx, NULL, xtstamp);
}
}
#endif
return ret;
}
+static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+ struct vmclock_crosststamp_ctx vctx = { .st = st };
+
+ return ptp_vmclock_do_getcrosststamp(&vctx, xtstamp);
+}
+
/*
* PTP clock operations
*/
@@ -311,7 +450,29 @@ static int ptp_vmclock_gettimex(struct ptp_clock_info *ptp, struct timespec64 *t
struct vmclock_state *st = container_of(ptp, struct vmclock_state,
ptp_clock_info);
- return vmclock_get_crosststamp(st, sts, NULL, ts);
+ return vmclock_get_crosststamp(st, sts, NULL, ts, NULL);
+}
+
+static int ptp_vmclock_gettimexattrs(struct ptp_clock_info *ptp,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts,
+ struct ptp_clock_attributes *att)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+
+ return vmclock_get_crosststamp(st, sts, NULL, ts, att);
+}
+
+static int ptp_vmclock_getcrosststampattrs(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp,
+ struct ptp_clock_attributes *att)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+ struct vmclock_crosststamp_ctx vctx = { .st = st, .attrs = att };
+
+ return ptp_vmclock_do_getcrosststamp(&vctx, xtstamp);
}
static int ptp_vmclock_enable(struct ptp_clock_info *ptp,
@@ -329,9 +490,11 @@ static const struct ptp_clock_info ptp_vmclock_info = {
.adjfine = ptp_vmclock_adjfine,
.adjtime = ptp_vmclock_adjtime,
.gettimex64 = ptp_vmclock_gettimex,
+ .gettimexattrs64 = ptp_vmclock_gettimexattrs,
.settime64 = ptp_vmclock_settime,
.enable = ptp_vmclock_enable,
.getcrosststamp = ptp_vmclock_getcrosststamp,
+ .getcrosststampattrs = ptp_vmclock_getcrosststampattrs,
};
static struct ptp_clock *vmclock_ptp_register(struct device *dev,
--
2.47.3
next prev parent reply other threads:[~2026-04-30 3:26 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-30 3:24 [PATCH v2 net-next 0/8] ptp: Add PHC timestamp quality attributes Arthur Kiyanovski
2026-04-30 3:24 ` [PATCH v2 net-next 1/8] ptp: Add ioctls for PHC timestamps with " Arthur Kiyanovski
2026-04-30 3:24 ` [PATCH v2 net-next 2/8] selftests/ptp: Extract print_system_timestamp helper in testptp Arthur Kiyanovski
2026-04-30 3:25 ` [PATCH v2 net-next 3/8] selftests/ptp: Add testptp support for attributes ioctls Arthur Kiyanovski
2026-04-30 3:25 ` Arthur Kiyanovski [this message]
2026-04-30 3:25 ` [PATCH v2 net-next 5/8] net: ena: PHC: Check return code before setting timestamp output Arthur Kiyanovski
2026-05-05 9:31 ` Simon Horman
2026-04-30 3:25 ` [PATCH v2 net-next 6/8] net: ena: Update PHC admin interface for error bound support Arthur Kiyanovski
2026-04-30 3:25 ` [PATCH v2 net-next 7/8] net: ena: Add error bound to PHC communication layer Arthur Kiyanovski
2026-04-30 3:25 ` [PATCH v2 net-next 8/8] net: ena: Implement gettimexattrs64 callback for PTP attributes Arthur Kiyanovski
2026-05-05 9:34 ` [PATCH v2 net-next 0/8] ptp: Add PHC timestamp quality attributes Simon Horman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260430032507.11586-5-akiyano@amazon.com \
--to=akiyano@amazon.com \
--cc=aliguori@amazon.com \
--cc=alisaidi@amazon.com \
--cc=amitbern@amazon.com \
--cc=andrew+netdev@lunn.ch \
--cc=benh@amazon.com \
--cc=darinzon@amazon.com \
--cc=davem@davemloft.net \
--cc=dwmw2@infradead.org \
--cc=dwmw@amazon.com \
--cc=edumazet@google.com \
--cc=evgenys@amazon.com \
--cc=evostrov@amazon.com \
--cc=guwen@linux.alibaba.com \
--cc=kuba@kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=matua@amazon.com \
--cc=mlichvar@redhat.com \
--cc=msw@amazon.com \
--cc=nafea@amazon.com \
--cc=ndagan@amazon.com \
--cc=netanel@amazon.com \
--cc=netdev@vger.kernel.org \
--cc=ofirt@amazon.com \
--cc=pabeni@redhat.com \
--cc=richardcochran@gmail.com \
--cc=saeedb@amazon.com \
--cc=shuah@kernel.org \
--cc=tglx@linutronix.de \
--cc=vadim.fedorenko@linux.dev \
--cc=xuanzhuo@linux.alibaba.com \
--cc=ysarna@amazon.com \
--cc=zorik@amazon.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox