Skip to content

Conversation

@zonque
Copy link
Contributor

@zonque zonque commented Jan 15, 2026

No description provided.

@zonque
Copy link
Contributor Author

zonque commented Jan 15, 2026

@davidv1992 See this as a draft - just an idea on how an API could look like. Users such as statime would then need to take the returned maximums into account for their steering loop in order to avoid OutOfRange errors.

@zonque
Copy link
Contributor Author

zonque commented Jan 15, 2026

Follow-up on #134

@zonque zonque force-pushed the clock-capabilities branch 2 times, most recently from 98f82cf to 8d2878a Compare January 15, 2026 15:12
Add a type to express a clock's capability of steering the clock and its
phase. For regular POSIX clocks, the reported values default to what the
Linux kernel is able to handle.

On the first call of `Clock::capabilities()`, and when executed on Linux,
the code will attempt to call the `PTP_CLOCK_GETCAPS` `ioctl()` once. In
case the file descriptor belongs to a PTP clock, this will return a
struct that tells us the capabilities of the underlying hardware.
Otherwise, we fall back to the POSIX defaults.
@zonque zonque force-pushed the clock-capabilities branch from 8d2878a to d832824 Compare January 15, 2026 15:34
@zonque zonque force-pushed the clock-capabilities branch from d832824 to dbd52c9 Compare January 15, 2026 15:51
@davidv1992
Copy link
Member

This PR moves clock_steering significantly closer to being a general use library. We are currently not setup to maintain it as such, so we'll need to have an internal discussion whether we want it to be that. This might take us a few weeks.

@zonque
Copy link
Contributor Author

zonque commented Jan 16, 2026

This PR moves clock_steering significantly closer to being a general use library. We are currently not setup to maintain it as such, so we'll need to have an internal discussion whether we want it to be that. This might take us a few weeks.

No worries, I'm not in a hurry. And I am also open to other suggestions. Bottom line however is that statime needs to consider the hardware's capabilities for its steering loop, one way or another.

Copy link
Member

@davidv1992 davidv1992 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've had the internal discussion, and decided to move forward with this.

Having a look, mostly this looks like a reasonable direction, however there are a number of smaller issues that need some attention, most importantly the fact that there currently seems to be going on a bit of unit confusion. If you can fix the things identified below we should most likely be good to go.


[dependencies]
libc = "0.2.165"
nix = { version = "0.27", features = ["ioctl"] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We conciously make the choice to not use nix, so as to avoid it and its dependencies for ntpd-rs. Could you add manual implementations for the ioctls where needed, or implement the needed functionality in libc?


Ok(ClockCapabilities {
max_frequency_adjustment_ppb: caps.max_adj as u64,
max_offset_adjustment_ns: caps.max_phase_adj as u32,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a clamped value instead of an outright cast.

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClockCapabilities {
/// Maximum frequency adjustment capability in parts per million.
pub max_frequency_adjustment_ppb: u64,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the units used in the rest of the interface for clocks (f64 respresenting desired frequency in ppm) it would be better to use that same unit here.

impl Default for ClockCapabilities {
fn default() -> Self {
Self {
max_frequency_adjustment_ppb: 32_768_000_000, // 32768000 ppm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value is incorrect, should match 500 ppm. Note that the kernel uses two units for frequency offsets, ppb and scaled ppm, where the latter is ppm multiplied by 2^16 (a.k.a. a fixed point format)

Comment on lines +45 to +53
/// Get the maximum frequency adjustment limit in ppm
pub const fn max_frequency_ppm(&self) -> u64 {
self.max_frequency_adjustment_ppb
}

/// Get the maximum offset adjustment limit in nanoseconds
pub const fn max_offset_ns(&self) -> u32 {
self.max_offset_adjustment_ns
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given what this struct is (a plain old data carrier) let's not do these functions.

Comment on lines +19 to +20
#[cfg(target_os = "linux")]
capabilities: Option<ClockCapabilities>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to want explicit caching for the clock capabilities, instead of just looking them up when asked and trusting the user to do any caching if need be?

Comment on lines +910 to +911
assert!(max_freq > 0);
assert!(max_freq >= 32768000); // At least 32768000 ppm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are redundant. Also let's just check that the actual value is correct, instead of a mere reasonability check, since this is a known clock with a guaranteed max frequency of 500ppm. (which also isn't the value tested for here).

@davidv1992
Copy link
Member

davidv1992 commented Jan 23, 2026

Forgot to asl this, but have you checked the reported limits on actual hardware? The header file suggests the max_freq_adj field to be in ppb, which is weird and inconsistent with the rest of the linux time APIs so having some confirmation this is actually correct would be nice. (not that it would be surprising that there is inconsistency here, the time APIs are notorious for being quite awkward, especially for ptp hardware clocks.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants