Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure hardware clock and hardware based timestamping automatically when available by default #549

Merged
merged 1 commit into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/man/statime.toml.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ will be indicated by each configuration setting shown.
`master-only` = *bool* (**false**)
: The port is always a master instance, and will never become a slave instance.

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

`acceptable-master-list` = [ *clock identity*, .. ] (**unset**)
: List of clock identities that this port will accept as its master.
Expand Down
2 changes: 1 addition & 1 deletion docs/precompiled/man/statime.8
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.\" Automatically generated by Pandoc 3.5
.\" Automatically generated by Pandoc 3.4
.\"
.TH "STATIME" "8" "" "statime 0.2.2" "statime"
.SH NAME
Expand Down
9 changes: 7 additions & 2 deletions docs/precompiled/man/statime.toml.5
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.\" Automatically generated by Pandoc 3.5
.\" Automatically generated by Pandoc 3.4
.\"
.TH "STATIME.TOML" "5" "" "statime 0.2.2" "statime"
.SH NAME
Expand Down Expand Up @@ -92,9 +92,14 @@ seconds
The port is always a master instance, and will never become a slave
instance.
.TP
\f[CR]hardware\-clock\f[R] = \f[I]index\f[R] (\f[B]unset\f[R])
\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])
Index of a hardware clock device, for instance \f[CR]0\f[R] for
\f[CR]/dev/ptp0\f[R].
Set to auto to automatically configure the hardware clock if one is
available.
Set to required if you need a hardware clock and want the configuration
to fail if one is not available.
Set to none to disable using a hardware clock.
.TP
\f[CR]acceptable\-master\-list\f[R] = [ \f[I]clock identity\f[R], .. ] (\f[B]unset\f[R])
List of clock identities that this port will accept as its master.
Expand Down
49 changes: 46 additions & 3 deletions statime-linux/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,54 @@ pub struct Config {
pub virtual_system_clock: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum HardwareClock {
/// Automatically use the (default) hardware clock for the interface
/// specified if available
#[default]
Auto,
/// Require the use of the (default) hardware clock for the interface
/// specified. If a hardware clock is not available, statime will refuse
/// to start.
Required,
/// Use the specified hardware clock
Specific(u32),
/// Do not use a hardware clock
None,
}

impl<'de> Deserialize<'de> for HardwareClock {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;

let raw: String = Deserialize::deserialize(deserializer)?;

if raw == "auto" {
Ok(HardwareClock::Auto)
} else if raw == "required" {
Ok(HardwareClock::Required)
} else if raw == "none" {
Ok(HardwareClock::None)
} else {
let clock = raw
.parse()
.map_err(|e| D::Error::custom(format!("Invalid hardware clock: {}", e)))?;
Ok(HardwareClock::Specific(clock))
}
}
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct PortConfig {
pub interface: InterfaceName,
#[serde(default, deserialize_with = "deserialize_acceptable_master_list")]
pub acceptable_master_list: Option<Vec<ClockIdentity>>,
#[serde(default)]
pub hardware_clock: Option<u32>,
pub hardware_clock: HardwareClock,
#[serde(default)]
pub network_mode: NetworkMode,
#[serde(default = "default_announce_interval")]
Expand Down Expand Up @@ -273,7 +313,10 @@ mod tests {
use statime::config::PtpMinorVersion;
use timestamped_socket::interface::InterfaceName;

use crate::{config::ObservabilityConfig, tracing::LogLevel};
use crate::{
config::{HardwareClock, ObservabilityConfig},
tracing::LogLevel,
};

// Minimal amount of config results in default values
#[test]
Expand All @@ -286,7 +329,7 @@ interface = "enp0s31f6"
let expected_port = crate::config::PortConfig {
interface: InterfaceName::from_str("enp0s31f6").unwrap(),
acceptable_master_list: None,
hardware_clock: None,
hardware_clock: HardwareClock::Auto,
network_mode: crate::config::NetworkMode::Ipv4,
announce_interval: 1,
sync_interval: 0,
Expand Down
64 changes: 40 additions & 24 deletions statime-linux/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use statime::{
};
use statime_linux::{
clock::{LinuxClock, PortTimestampToTime},
config::HardwareClock,
initialize_logging_parse_config,
observer::ObservableInstanceState,
socket::{
Expand Down Expand Up @@ -314,38 +315,53 @@ async fn actual_main() {

let tlv_forwarder = TlvForwarder::new();

let mut add_hw_clock = |idx: u32, clock_port_map: &mut Vec<Option<usize>>| {
let mut clock = LinuxClock::open_idx(idx).expect("Unable to open clock");
if let Some(id) = clock_name_map.get(&idx) {
clock_port_map.push(Some(*id));
} else {
clock.init().expect("Unable to initialize clock");
let id = internal_sync_senders.len();
clock_port_map.push(Some(id));
clock_name_map.insert(idx, id);
internal_sync_senders.push(start_clock_task(clock.clone(), system_clock.clone()));
}
(
Some(idx),
Box::new(clock) as BoxedClock,
InterfaceTimestampMode::HardwarePTPAll,
)
};

let add_sw_clock = |clock_port_map: &mut Vec<Option<usize>>| {
clock_port_map.push(None);
(
None,
system_clock.clone_boxed(),
InterfaceTimestampMode::SoftwareAll,
)
};

for port_config in config.ports {
let interface = port_config.interface;
let network_mode = port_config.network_mode;
let (port_clock, timestamping) = match port_config.hardware_clock {
Some(idx) => {
let mut clock = LinuxClock::open_idx(idx).expect("Unable to open clock");
if let Some(id) = clock_name_map.get(&idx) {
clock_port_map.push(Some(*id));
} else {
clock.init().expect("Unable to initialize clock");
let id = internal_sync_senders.len();
clock_port_map.push(Some(id));
clock_name_map.insert(idx, id);
internal_sync_senders
.push(start_clock_task(clock.clone(), system_clock.clone()));
let (bind_phc, port_clock, timestamping) = match port_config.hardware_clock {
HardwareClock::Auto => match interface.lookup_phc() {
Some(idx) => add_hw_clock(idx, &mut clock_port_map),
None => {
log::info!("No hardware clock found, falling back to software timestamping");
add_sw_clock(&mut clock_port_map)
}
(
Box::new(clock) as BoxedClock,
InterfaceTimestampMode::HardwarePTPAll,
)
}
None => {
clock_port_map.push(None);
(
system_clock.clone_boxed(),
InterfaceTimestampMode::SoftwareAll,
)
},
HardwareClock::Required => {
let idx = interface.lookup_phc().expect("No hardware clock found");
add_hw_clock(idx, &mut clock_port_map)
}
HardwareClock::Specific(idx) => add_hw_clock(idx, &mut clock_port_map),
HardwareClock::None => add_sw_clock(&mut clock_port_map),
};

let rng = StdRng::from_entropy();
let bind_phc = port_config.hardware_clock;
let port = instance.add_port(
port_config.into(),
KalmanConfiguration::default(),
Expand Down
4 changes: 2 additions & 2 deletions statime/src/port/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub(crate) mod state;
/// # }
/// # let (instance_config, time_properties_ds) = unimplemented!();
/// use rand::thread_rng;
/// use statime::config::{AcceptAnyMaster, DelayMechanism, PortConfig};
/// use statime::config::{AcceptAnyMaster, DelayMechanism, PortConfig, PtpMinorVersion};
/// use statime::filters::BasicFilter;
/// use statime::PtpInstance;
/// use statime::time::Interval;
Expand All @@ -135,7 +135,7 @@ pub(crate) mod state;
/// sync_interval: interval,
/// master_only: false,
/// delay_asymmetry: Default::default(),
/// minor_ptp_version: 1,
/// minor_ptp_version: PtpMinorVersion::One,
/// };
/// let filter_config = 1.0;
/// let clock = system::Clock {};
Expand Down
Loading