// Module included in the following assemblies:
//
// * networking/ptp/configuring-ptp.adoc

:_mod-docs-content-type: PROCEDURE
[id="nw-ptp-t-bc-t-tsc-holdover_{context}"]
= Applying unassisted holdover for boundary clocks and time synchronous clocks

[role="_abstract"]
The unassisted holdover feature enables an Intel E810-XXVDA4T NIC, configured as a PTP boundary clock (T-BC) or telecom time synchronous clock (T-TSC), to maintain time synchronization when the upstream timing source becomes unavailable.

When the upstream source degrades, disconnects, or fails, the NIC enters the holdover state. In this state, the NIC relies on its internal oscillator to maintain accurate time autonomously. On nodes with multiple NICs, one NIC acts as the leading NIC. Other NICs on the node synchronize to the leading NIC through SMA cable connections managed by the `ts2phc` service.

In T-BC configurations, the time transmitter (TT) ports continue transmitting timing to downstream devices, signaling a degraded but usable clock quality. Timing remains stable on both the local node and the downstream network until the upstream source recovers or the holdover timeout expires.

This example describes how to configure unassisted holdover for a multi-card T-BC configuration with three NICs.

.Prerequisites
* Install the OpenShift CLI (`oc`).
* Log in as a user with `cluster-admin` privileges.
* Install the PTP Operator.
* One or more Intel E810-XXVDA4T NICs.
* For multi-card configurations, SMA cables connecting the NICs.

.Procedure

. Create a `PtpConfig` CR with two profiles: one for time transmitter (TT) ports and one for the time receiver (TR) port with hardware configuration.

.. Define the TT profile that configures downstream-facing ports:
+
[source,yaml]
----
apiVersion: ptp.openshift.io/v1
kind: PtpConfig
metadata:
  name: t-bc
  namespace: openshift-ptp
spec:
  profile:
  - name: 00-tbc-tt
    ptp4lConf: |
      [ens4f0]
      masterOnly 1
      [ens8f0]
      masterOnly 1
      [ens1f0]
      masterOnly 1
      [global]
      #
      # Default Data Set
      #
      twoStepFlag 1
      slaveOnly 0
      priority1 128
      priority2 128
      domainNumber 25
      clockClass 248
      clockAccuracy 0xFE
      offsetScaledLogVariance 0xFFFF
      free_running 0
      freq_est_interval 1
      dscp_event 0
      dscp_general 0
      dataset_comparison G.8275.x
      G.8275.defaultDS.localPriority 128
      #
      # Port Data Set
      #
      logAnnounceInterval -3
      logSyncInterval -4
      logMinDelayReqInterval -4
      logMinPdelayReqInterval -4
      announceReceiptTimeout 3
      syncReceiptTimeout 0
      delayAsymmetry 0
      fault_reset_interval -4
      neighborPropDelayThresh 20000000
      masterOnly 0
      G.8275.portDS.localPriority 128
      #
      # Run time options
      #
      assume_two_step 0
      logging_level 6
      path_trace_enabled 0
      follow_up_info 0
      hybrid_e2e 0
      inhibit_multicast_service 0
      net_sync_monitor 0
      tc_spanning_tree 0
      tx_timestamp_timeout 50
      unicast_listen 0
      unicast_master_table 0
      unicast_req_duration 3600
      use_syslog 1
      verbose 0
      summary_interval 0
      kernel_leap 1
      check_fup_sync 0
      clock_class_threshold 135
      #
      # Servo Options
      #
      pi_proportional_const 0.60
      pi_integral_const 0.001
      pi_proportional_scale 0.0
      pi_proportional_exponent -0.3
      pi_proportional_norm_max 0.7
      pi_integral_scale 0.0
      pi_integral_exponent 0.4
      pi_integral_norm_max 0.3
      step_threshold 2.0
      first_step_threshold 0.00002
      max_frequency 900000000
      clock_servo pi
      sanity_freq_limit 200000000
      ntpshm_segment 0
      #
      # Transport options
      #
      transportSpecific 0x0
      ptp_dst_mac AA:BB:CC:DD:EE:FF
      p2p_dst_mac BB:CC:DD:EE:FF:GG
      udp_ttl 1
      udp6_scope 0x0E
      uds_address /var/run/ptp4l
      #
      # Default interface options
      #
      clock_type BC
      network_transport L2
      delay_mechanism E2E
      time_stamping hardware
      tsproc_mode filter
      delay_filter moving_median
      delay_filter_length 10
      egressLatency 0
      ingressLatency 0
      boundary_clock_jbod 1
      #
      # Clock description
      #
      productDescription ;;
      revisionData ;;
      manufacturerIdentity 00:00:00
      userDescription ;
      timeSource 0xA0
    ptp4lOpts: -2 --summary_interval -4
    ptpSchedulingPolicy: SCHED_FIFO
    ptpSchedulingPriority: 10
    ptpSettings:
      controllingProfile: 01-tbc-tr
      logReduce: "false"
# ...
----
* `spec.profile[].ptp4lConf` defines the `ptp4l` configuration. In the `[ens4f0]` interface section, the `masterOnly 1` setting configures TT ports to transmit timing downstream.
* `clock_type BC` sets the clock to boundary clock operation.
* `boundary_clock_jbod 1` enables multi-NIC configurations.
* `spec.profile[].ptpSettings.controllingProfile` references the TR profile that controls holdover behavior.

.. Define the TR profile with the hardware plugin configuration:
+
[source,yaml]
----
# ...
  - name: 01-tbc-tr
    phc2sysOpts: -r -n 25 -N 8 -R 16 -u 0 -m -s ens4f1
    plugins:
      e810:
        enableDefaultConfig: false
        interconnections:
        - gnssInput: false
          id: ens4f0
          part: E810-XXVDA4T
          phaseOutputConnectors:
          - SMA1
          - SMA2
          upstreamPort: ens4f1
        - id: ens1f0
          inputConnector:
            connector: SMA1
          part: E810-XXVDA4T
        - id: ens8f0
          inputConnector:
            connector: SMA1
          part: E810-XXVDA4T
        pins:
          ens4f0:
            SMA1: 2 1
            SMA2: 2 2
            U.FL1: 0 1
            U.FL2: 0 2
          ens1f0:
            SMA1: 1 1
            SMA2: 0 2
            U.FL1: 0 1
            U.FL2: 0 2
          ens8f0:
            SMA1: 1 1
            SMA2: 0 2
            U.FL1: 0 1
            U.FL2: 0 2
        settings:
          LocalHoldoverTimeout: 14400
          LocalMaxHoldoverOffSet: 1500
          MaxInSpecOffset: 100
# ...
----
* `phc2sysOpts` specifies the upstream port (`ens4f1`) as the source for system clock synchronization.
* `plugins.e810.interconnections` defines how NICs are connected. The leading NIC (`ens4f0`) outputs phase to other NICs through SMA cables.
* `plugins.e810.interconnections[].phaseOutputConnectors` lists SMA connectors used for cable connections to downstream NICs.
* `plugins.e810.interconnections[].upstreamPort` specifies the TR port that receives timing from upstream. Set this for both T-BC and T-TSC configurations.
* `plugins.e810.pins` configures SMA and U.FL pin directions for each NIC.
* `plugins.e810.settings` configures holdover behavior. For details about these parameters, see "Holdover in a grandmaster clock with GNSS as the source".

.. Configure the `ptp4lConf` for the TR port:
+
[source,yaml]
----
# ...
    ptp4lConf: |
      [ens4f1]
      masterOnly 0
      [global]
      #
      # Default Data Set
      #
      twoStepFlag 1
      slaveOnly 0
      priority1 128
      priority2 128
      domainNumber 25
      clockClass 248
      clockAccuracy 0xFE
      offsetScaledLogVariance 0xFFFF
      free_running 0
      freq_est_interval 1
      dscp_event 0
      dscp_general 0
      dataset_comparison G.8275.x
      G.8275.defaultDS.localPriority 128
      #
      # Port Data Set
      #
      logAnnounceInterval -3
      logSyncInterval -4
      logMinDelayReqInterval -4
      logMinPdelayReqInterval -4
      announceReceiptTimeout 3
      syncReceiptTimeout 0
      delayAsymmetry 0
      fault_reset_interval -4
      neighborPropDelayThresh 20000000
      masterOnly 0
      G.8275.portDS.localPriority 128
      #
      # Run time options
      #
      assume_two_step 0
      logging_level 6
      path_trace_enabled 0
      follow_up_info 0
      hybrid_e2e 0
      inhibit_multicast_service 0
      net_sync_monitor 0
      tc_spanning_tree 0
      tx_timestamp_timeout 50
      unicast_listen 0
      unicast_master_table 0
      unicast_req_duration 3600
      use_syslog 1
      verbose 0
      summary_interval 0
      kernel_leap 1
      check_fup_sync 0
      clock_class_threshold 135
      #
      # Servo Options
      #
      pi_proportional_const 0.60
      pi_integral_const 0.001
      pi_proportional_scale 0.0
      pi_proportional_exponent -0.3
      pi_proportional_norm_max 0.7
      pi_integral_scale 0.0
      pi_integral_exponent 0.4
      pi_integral_norm_max 0.3
      step_threshold 2.0
      first_step_threshold 0.00002
      max_frequency 900000000
      clock_servo pi
      sanity_freq_limit 200000000
      ntpshm_segment 0
      #
      # Transport options
      #
      transportSpecific 0x0
      ptp_dst_mac AA:BB:CC:DD:EE:HH
      p2p_dst_mac BB:CC:DD:EE:FF:II
      udp_ttl 1
      udp6_scope 0x0E
      uds_address /var/run/ptp4l
      #
      # Default interface options
      #
      clock_type OC
      network_transport L2
      delay_mechanism E2E
      time_stamping hardware
      tsproc_mode filter
      delay_filter moving_median
      delay_filter_length 10
      egressLatency 0
      ingressLatency 0
      boundary_clock_jbod 1
      #
      # Clock description
      #
      productDescription ;;
      revisionData ;;
      manufacturerIdentity 00:00:00
      userDescription ;
      timeSource 0xA0
    ptp4lOpts: -2 --summary_interval -4
    ptpSchedulingPolicy: SCHED_FIFO
    ptpSchedulingPriority: 10
    ptpSettings:
      inSyncConditionThreshold: "10"
      inSyncConditionTimes: "12"
      logReduce: "false"
# ...
----
* `spec.profile[].ptp4lConf` defines the `ptp4l` configuration. In the `[ens4f1]` interface section, the `masterOnly 0` setting configures the TR port to receive timing from upstream.
* `clock_type OC` sets the TR profile to ordinary clock because it handles only the upstream-facing port.

.. Configure `ts2phc` for all participating NICs:
+
[source,yaml]
----
# ...
    ts2phcConf: |
      [global]
      use_syslog
      verbose 1
      logging_level 7
      ts2phc.pulsewidth 100000000
      leapfile  /usr/share/zoneinfo/leap-seconds.list
      domainNumber 25
      uds_address /var/run/ptp4l.0.socket
      [ens4f0]
      ts2phc.extts_polarity rising
      ts2phc.extts_correction -10
      ts2phc.master 0
      [ens1f0]
      ts2phc.extts_polarity rising
      ts2phc.extts_correction -27
      ts2phc.master 0
      [ens8f0]
      ts2phc.extts_polarity rising
      ts2phc.extts_correction -27
      ts2phc.master 0
    ts2phcOpts: -s generic -a --ts2phc.rh_external_pps 1
# ...
----
* The `domainNumber` must match the upstream PTP domain.
* Interface sections like `[ens4f0]` list all NICs participating in the configuration. The `ts2phc.extts_correction` values compensate for cable and hardware delays.

.. Define the `recommend` section to apply profiles to nodes:
+
[source,yaml]
----
# ...
  recommend:
  - match:
    - nodeLabel: node-role.kubernetes.io/master
    priority: 4
    profile: 00-tbc-tt
  - match:
    - nodeLabel: node-role.kubernetes.io/master
    priority: 4
    profile: 01-tbc-tr
----

. Review the full configuration:
+
[source,yaml]
----
apiVersion: ptp.openshift.io/v1
kind: PtpConfig
metadata:
  name: t-bc
  namespace: openshift-ptp
spec:
  profile:
  - name: 00-tbc-tt
    ptp4lConf: |
      [ens4f0]
      masterOnly 1
      [ens8f0]
      masterOnly 1
      [ens1f0]
      masterOnly 1
      [global]
      #
      # Default Data Set
      #
      twoStepFlag 1
      slaveOnly 0
      priority1 128
      priority2 128
      domainNumber 25
      clockClass 248
      clockAccuracy 0xFE
      offsetScaledLogVariance 0xFFFF
      free_running 0
      freq_est_interval 1
      dscp_event 0
      dscp_general 0
      dataset_comparison G.8275.x
      G.8275.defaultDS.localPriority 128
      #
      # Port Data Set
      #
      logAnnounceInterval -3
      logSyncInterval -4
      logMinDelayReqInterval -4
      logMinPdelayReqInterval -4
      announceReceiptTimeout 3
      syncReceiptTimeout 0
      delayAsymmetry 0
      fault_reset_interval -4
      neighborPropDelayThresh 20000000
      masterOnly 0
      G.8275.portDS.localPriority 128
      #
      # Run time options
      #
      assume_two_step 0
      logging_level 6
      path_trace_enabled 0
      follow_up_info 0
      hybrid_e2e 0
      inhibit_multicast_service 0
      net_sync_monitor 0
      tc_spanning_tree 0
      tx_timestamp_timeout 50
      unicast_listen 0
      unicast_master_table 0
      unicast_req_duration 3600
      use_syslog 1
      verbose 0
      summary_interval 0
      kernel_leap 1
      check_fup_sync 0
      clock_class_threshold 135
      #
      # Servo Options
      #
      pi_proportional_const 0.60
      pi_integral_const 0.001
      pi_proportional_scale 0.0
      pi_proportional_exponent -0.3
      pi_proportional_norm_max 0.7
      pi_integral_scale 0.0
      pi_integral_exponent 0.4
      pi_integral_norm_max 0.3
      step_threshold 2.0
      first_step_threshold 0.00002
      max_frequency 900000000
      clock_servo pi
      sanity_freq_limit 200000000
      ntpshm_segment 0
      #
      # Transport options
      #
      transportSpecific 0x0
      ptp_dst_mac AA:BB:CC:DD:EE:FF
      p2p_dst_mac BB:CC:DD:EE:FF:GG
      udp_ttl 1
      udp6_scope 0x0E
      uds_address /var/run/ptp4l
      #
      # Default interface options
      #
      clock_type BC
      network_transport L2
      delay_mechanism E2E
      time_stamping hardware
      tsproc_mode filter
      delay_filter moving_median
      delay_filter_length 10
      egressLatency 0
      ingressLatency 0
      boundary_clock_jbod 1
      #
      # Clock description
      #
      productDescription ;;
      revisionData ;;
      manufacturerIdentity 00:00:00
      userDescription ;
      timeSource 0xA0
    ptp4lOpts: -2 --summary_interval -4
    ptpSchedulingPolicy: SCHED_FIFO
    ptpSchedulingPriority: 10
    ptpSettings:
      controllingProfile: 01-tbc-tr
      logReduce: "false"
  - name: 01-tbc-tr
    phc2sysOpts: -r -n 25 -N 8 -R 16 -u 0 -m -s ens4f1
    plugins:
      e810:
        enableDefaultConfig: false
        interconnections:
        - gnssInput: false
          id: ens4f0
          part: E810-XXVDA4T
          phaseOutputConnectors:
          - SMA1
          - SMA2
          upstreamPort: ens4f1
        - id: ens1f0
          inputConnector:
            connector: SMA1
          part: E810-XXVDA4T
        - id: ens8f0
          inputConnector:
            connector: SMA1
          part: E810-XXVDA4T
        pins:
          ens4f0:
            SMA1: 2 1
            SMA2: 2 2
            U.FL1: 0 1
            U.FL2: 0 2
          ens1f0:
            SMA1: 1 1
            SMA2: 0 2
            U.FL1: 0 1
            U.FL2: 0 2
          ens8f0:
            SMA1: 1 1
            SMA2: 0 2
            U.FL1: 0 1
            U.FL2: 0 2
        settings:
          LocalHoldoverTimeout: 14400
          LocalMaxHoldoverOffSet: 1500
          MaxInSpecOffset: 100
    ptp4lConf: |
      [ens4f1]
      masterOnly 0
      [global]
      #
      # Default Data Set
      #
      twoStepFlag 1
      slaveOnly 0
      priority1 128
      priority2 128
      domainNumber 25
      clockClass 248
      clockAccuracy 0xFE
      offsetScaledLogVariance 0xFFFF
      free_running 0
      freq_est_interval 1
      dscp_event 0
      dscp_general 0
      dataset_comparison G.8275.x
      G.8275.defaultDS.localPriority 128
      #
      # Port Data Set
      #
      logAnnounceInterval -3
      logSyncInterval -4
      logMinDelayReqInterval -4
      logMinPdelayReqInterval -4
      announceReceiptTimeout 3
      syncReceiptTimeout 0
      delayAsymmetry 0
      fault_reset_interval -4
      neighborPropDelayThresh 20000000
      masterOnly 0
      G.8275.portDS.localPriority 128
      #
      # Run time options
      #
      assume_two_step 0
      logging_level 6
      path_trace_enabled 0
      follow_up_info 0
      hybrid_e2e 0
      inhibit_multicast_service 0
      net_sync_monitor 0
      tc_spanning_tree 0
      tx_timestamp_timeout 50
      unicast_listen 0
      unicast_master_table 0
      unicast_req_duration 3600
      use_syslog 1
      verbose 0
      summary_interval 0
      kernel_leap 1
      check_fup_sync 0
      clock_class_threshold 135
      #
      # Servo Options
      #
      pi_proportional_const 0.60
      pi_integral_const 0.001
      pi_proportional_scale 0.0
      pi_proportional_exponent -0.3
      pi_proportional_norm_max 0.7
      pi_integral_scale 0.0
      pi_integral_exponent 0.4
      pi_integral_norm_max 0.3
      step_threshold 2.0
      first_step_threshold 0.00002
      max_frequency 900000000
      clock_servo pi
      sanity_freq_limit 200000000
      ntpshm_segment 0
      #
      # Transport options
      #
      transportSpecific 0x0
      ptp_dst_mac AA:BB:CC:DD:EE:HH
      p2p_dst_mac BB:CC:DD:EE:FF:II
      udp_ttl 1
      udp6_scope 0x0E
      uds_address /var/run/ptp4l
      #
      # Default interface options
      #
      clock_type OC
      network_transport L2
      delay_mechanism E2E
      time_stamping hardware
      tsproc_mode filter
      delay_filter moving_median
      delay_filter_length 10
      egressLatency 0
      ingressLatency 0
      boundary_clock_jbod 1
      #
      # Clock description
      #
      productDescription ;;
      revisionData ;;
      manufacturerIdentity 00:00:00
      userDescription ;
      timeSource 0xA0
    ptp4lOpts: -2 --summary_interval -4
    ptpSchedulingPolicy: SCHED_FIFO
    ptpSchedulingPriority: 10
    ptpSettings:
      inSyncConditionThreshold: "10"
      inSyncConditionTimes: "12"
      logReduce: "false"
    ts2phcConf: |
      [global]
      use_syslog  0
      verbose 1
      logging_level 7
      ts2phc.pulsewidth 100000000
      leapfile  /usr/share/zoneinfo/leap-seconds.list
      domainNumber 25
      uds_address /var/run/ptp4l.0.socket
      [ens4f0]
      ts2phc.extts_polarity rising
      ts2phc.extts_correction -10
      ts2phc.master 0
      [ens1f0]
      ts2phc.extts_polarity rising
      ts2phc.extts_correction -27
      ts2phc.master 0
      [ens8f0]
      ts2phc.extts_polarity rising
      ts2phc.extts_correction -27
      ts2phc.master 0
    ts2phcOpts: -s generic -a --ts2phc.rh_external_pps 1
  recommend:
  - match:
    - nodeLabel: node-role.kubernetes.io/master
    priority: 4
    profile: 00-tbc-tt
  - match:
    - nodeLabel: node-role.kubernetes.io/master
    priority: 4
    profile: 01-tbc-tr
----
+
[NOTE]
====
This example shows a multi-card T-BC configuration with three NICs. To adapt for other scenarios:

T-TSC:

* Remove the `00-tbc-tt` profile entirely
* Set `clock_type` to `OC` in the TR profile
* In `ts2phcConf`, list only the TR NIC

Single-card T-BC:

* In the TT profile, list only ports from the same NIC as the TR port
* Remove additional NIC entries from the `interconnections` and `pins` sections
* In `ts2phcConf`, list only the single NIC
* Set `phaseOutputConnectors` to an empty array or disable all pins if not using SMA outputs
====

. Save the configuration to a file, for example `t-bc-holdover-config.yaml`, and apply it:
+
[source,terminal]
----
$ oc apply -f t-bc-holdover-config.yaml
----

.Verification

* Get the status of the T-BC by running the following command:
+
[source,terminal]
----
$ oc logs ds/linuxptp-daemon -n openshift-ptp -c linuxptp-daemon-container --since=1s -f | grep T-BC
----
+
.Example output
[source,terminal]
----
T-BC[1760525446]:[ts2phc.1.config] ens4f0 offset 1 T-BC-STATUS s2
T-BC[1760525447]:[ts2phc.1.config] ens4f0 offset 1 T-BC-STATUS s2
T-BC[1760525448]:[ts2phc.1.config] ens4f0 offset -1 T-BC-STATUS s2
----
+
The status is reported every second:
+
* `s2`: Locked - synchronized with the upstream clock
* `s1`: Holdover - upstream source lost, internal oscillator maintaining timing
* `s0`: Unlocked - holdover limits exceeded or no valid holdover data
