Skip to content

Commit 6cf8cd8

Browse files
rnijvelddavidv1992
authored andcommitted
Configure hardware clock and hardware based timestamping automatically when available by default
1 parent 6610c64 commit 6cf8cd8

File tree

5 files changed

+99
-32
lines changed

5 files changed

+99
-32
lines changed

docs/man/statime.toml.5.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@ will be indicated by each configuration setting shown.
7979
`master-only` = *bool* (**false**)
8080
: The port is always a master instance, and will never become a slave instance.
8181

82-
`hardware-clock` = *index* (**unset**)
83-
: Index of a hardware clock device, for instance `0` for `/dev/ptp0`.
82+
`hardware-clock` = `auto` | `required` | `none` | *index* (**auto**)
83+
: Index of a hardware clock device, for instance `0` for `/dev/ptp0`. Set to
84+
auto to automatically configure the hardware clock if one is available. Set
85+
to required if you need a hardware clock and want the configuration to fail
86+
if one is not available. Set to none to disable using a hardware clock.
8487

8588
`acceptable-master-list` = [ *clock identity*, .. ] (**unset**)
8689
: List of clock identities that this port will accept as its master.

docs/precompiled/man/statime.toml.5

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,14 @@ seconds
9595
The port is always a master instance, and will never become a slave
9696
instance.
9797
.TP
98-
\f[CR]hardware\-clock\f[R] = \f[I]index\f[R] (\f[B]unset\f[R])
98+
\f[CR]hardware\-clock\f[R] = \f[CR]auto\f[R] | \f[CR]required\f[R] | \f[CR]none\f[R] | \f[I]index\f[R] (\f[B]auto\f[R])
9999
Index of a hardware clock device, for instance \f[CR]0\f[R] for
100100
\f[CR]/dev/ptp0\f[R].
101+
Set to auto to automatically configure the hardware clock if one is
102+
available.
103+
Set to required if you need a hardware clock and want the configuration
104+
to fail if one is not available.
105+
Set to none to disable using a hardware clock.
101106
.TP
102107
\f[CR]acceptable\-master\-list\f[R] = [ \f[I]clock identity\f[R], .. ] (\f[B]unset\f[R])
103108
List of clock identities that this port will accept as its master.

statime-linux/src/config/mod.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,54 @@ pub struct Config {
4242
pub virtual_system_clock: bool,
4343
}
4444

45+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
46+
pub enum HardwareClock {
47+
/// Automatically use the (default) hardware clock for the interface
48+
/// specified if available
49+
#[default]
50+
Auto,
51+
/// Require the use of the (default) hardware clock for the interface
52+
/// specified. If a hardware clock is not available, statime will refuse
53+
/// to start.
54+
Required,
55+
/// Use the specified hardware clock
56+
Specific(u32),
57+
/// Do not use a hardware clock
58+
None,
59+
}
60+
61+
impl<'de> Deserialize<'de> for HardwareClock {
62+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63+
where
64+
D: Deserializer<'de>,
65+
{
66+
use serde::de::Error;
67+
68+
let raw: String = Deserialize::deserialize(deserializer)?;
69+
70+
if raw == "auto" {
71+
Ok(HardwareClock::Auto)
72+
} else if raw == "required" {
73+
Ok(HardwareClock::Required)
74+
} else if raw == "none" {
75+
Ok(HardwareClock::None)
76+
} else {
77+
let clock = raw
78+
.parse()
79+
.map_err(|e| D::Error::custom(format!("Invalid hardware clock: {}", e)))?;
80+
Ok(HardwareClock::Specific(clock))
81+
}
82+
}
83+
}
84+
4585
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
4686
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
4787
pub struct PortConfig {
4888
pub interface: InterfaceName,
4989
#[serde(default, deserialize_with = "deserialize_acceptable_master_list")]
5090
pub acceptable_master_list: Option<Vec<ClockIdentity>>,
5191
#[serde(default)]
52-
pub hardware_clock: Option<u32>,
92+
pub hardware_clock: HardwareClock,
5393
#[serde(default)]
5494
pub network_mode: NetworkMode,
5595
#[serde(default = "default_announce_interval")]
@@ -279,7 +319,10 @@ mod tests {
279319
use statime::config::PtpMinorVersion;
280320
use timestamped_socket::interface::InterfaceName;
281321

282-
use crate::{config::ObservabilityConfig, tracing::LogLevel};
322+
use crate::{
323+
config::{HardwareClock, ObservabilityConfig},
324+
tracing::LogLevel,
325+
};
283326

284327
// Minimal amount of config results in default values
285328
#[test]
@@ -292,7 +335,7 @@ interface = "enp0s31f6"
292335
let expected_port = crate::config::PortConfig {
293336
interface: InterfaceName::from_str("enp0s31f6").unwrap(),
294337
acceptable_master_list: None,
295-
hardware_clock: None,
338+
hardware_clock: HardwareClock::Auto,
296339
network_mode: crate::config::NetworkMode::Ipv4,
297340
announce_interval: 1,
298341
sync_interval: 0,

statime-linux/src/main.rs

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use statime::{
2020
};
2121
use statime_linux::{
2222
clock::{LinuxClock, PortTimestampToTime},
23+
config::HardwareClock,
2324
initialize_logging_parse_config,
2425
observer::ObservableInstanceState,
2526
socket::{
@@ -314,38 +315,53 @@ async fn actual_main() {
314315

315316
let tlv_forwarder = TlvForwarder::new();
316317

318+
let mut add_hw_clock = |idx: u32, clock_port_map: &mut Vec<Option<usize>>| {
319+
let mut clock = LinuxClock::open_idx(idx).expect("Unable to open clock");
320+
if let Some(id) = clock_name_map.get(&idx) {
321+
clock_port_map.push(Some(*id));
322+
} else {
323+
clock.init().expect("Unable to initialize clock");
324+
let id = internal_sync_senders.len();
325+
clock_port_map.push(Some(id));
326+
clock_name_map.insert(idx, id);
327+
internal_sync_senders.push(start_clock_task(clock.clone(), system_clock.clone()));
328+
}
329+
(
330+
Some(idx),
331+
Box::new(clock) as BoxedClock,
332+
InterfaceTimestampMode::HardwarePTPAll,
333+
)
334+
};
335+
336+
let add_sw_clock = |clock_port_map: &mut Vec<Option<usize>>| {
337+
clock_port_map.push(None);
338+
(
339+
None,
340+
system_clock.clone_boxed(),
341+
InterfaceTimestampMode::SoftwareAll,
342+
)
343+
};
344+
317345
for port_config in config.ports {
318346
let interface = port_config.interface;
319347
let network_mode = port_config.network_mode;
320-
let (port_clock, timestamping) = match port_config.hardware_clock {
321-
Some(idx) => {
322-
let mut clock = LinuxClock::open_idx(idx).expect("Unable to open clock");
323-
if let Some(id) = clock_name_map.get(&idx) {
324-
clock_port_map.push(Some(*id));
325-
} else {
326-
clock.init().expect("Unable to initialize clock");
327-
let id = internal_sync_senders.len();
328-
clock_port_map.push(Some(id));
329-
clock_name_map.insert(idx, id);
330-
internal_sync_senders
331-
.push(start_clock_task(clock.clone(), system_clock.clone()));
348+
let (bind_phc, port_clock, timestamping) = match port_config.hardware_clock {
349+
HardwareClock::Auto => match interface.lookup_phc() {
350+
Some(idx) => add_hw_clock(idx, &mut clock_port_map),
351+
None => {
352+
log::info!("No hardware clock found, falling back to software timestamping");
353+
add_sw_clock(&mut clock_port_map)
332354
}
333-
(
334-
Box::new(clock) as BoxedClock,
335-
InterfaceTimestampMode::HardwarePTPAll,
336-
)
337-
}
338-
None => {
339-
clock_port_map.push(None);
340-
(
341-
system_clock.clone_boxed(),
342-
InterfaceTimestampMode::SoftwareAll,
343-
)
355+
},
356+
HardwareClock::Required => {
357+
let idx = interface.lookup_phc().expect("No hardware clock found");
358+
add_hw_clock(idx, &mut clock_port_map)
344359
}
360+
HardwareClock::Specific(idx) => add_hw_clock(idx, &mut clock_port_map),
361+
HardwareClock::None => add_sw_clock(&mut clock_port_map),
345362
};
346363

347364
let rng = StdRng::from_entropy();
348-
let bind_phc = port_config.hardware_clock;
349365
let port = instance.add_port(
350366
port_config.into(),
351367
KalmanConfiguration::default(),

statime/src/port/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ pub(crate) mod state;
118118
/// # }
119119
/// # let (instance_config, time_properties_ds) = unimplemented!();
120120
/// use rand::thread_rng;
121-
/// use statime::config::{AcceptAnyMaster, DelayMechanism, PortConfig};
121+
/// use statime::config::{AcceptAnyMaster, DelayMechanism, PortConfig, PtpMinorVersion};
122122
/// use statime::filters::BasicFilter;
123123
/// use statime::PtpInstance;
124124
/// use statime::time::Interval;
@@ -135,7 +135,7 @@ pub(crate) mod state;
135135
/// sync_interval: interval,
136136
/// master_only: false,
137137
/// delay_asymmetry: Default::default(),
138-
/// minor_ptp_version: 1,
138+
/// minor_ptp_version: PtpMinorVersion::One,
139139
/// };
140140
/// let filter_config = 1.0;
141141
/// let clock = system::Clock {};

0 commit comments

Comments
 (0)