Skip to content

Commit bf4bf09

Browse files
jacob-kellerJeff Kirsher
authored andcommitted
i40e: save PTP time before a device reset
In the case where PTP is running on the hardware clock, but the kernel system time is not being synced, a device reset can mess up the clock time. This occurs because we reset the clock time based on the kernel time every reset. This causes us to potentially completely reset the PTP time, and can cause unexpected behavior in programs like ptp4l. Avoid this by saving the PTP time prior to device reset, and then restoring using that time after the reset. Directly restoring the PTP time we saved isn't perfect, because time should have continued running, but the clock will essentially be stopped during the reset. This is still better than the current solution of assuming that the PTP HW clock is synced to the CLOCK_REALTIME. We can do even better, by saving the ktime and calculating a differential, using ktime_get(). This is based on CLOCK_MONOTONIC, and allows us to get a fairly precise measure of the time difference between saving and restoring the time. Using this, we can update the saved PTP time, and use that as the value to write to the hardware clock registers. This, of course is not perfect. However, it does help ensure that the PTP time is restored as close as feasible to the time it should have been if the reset had not occurred. During device initialization, continue using the system time as the source for the creation of the PTP clock, since this is the best known current time source at driver load. Signed-off-by: Jacob Keller <[email protected]> Signed-off-by: Jeff Kirsher <[email protected]>
1 parent bfb0ebe commit bf4bf09

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

drivers/net/ethernet/intel/i40e/i40e.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ struct i40e_pf {
612612
struct sk_buff *ptp_tx_skb;
613613
unsigned long ptp_tx_start;
614614
struct hwtstamp_config tstamp_config;
615+
struct timespec64 ptp_prev_hw_time;
616+
ktime_t ptp_reset_start;
615617
struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */
616618
u32 ptp_adj_mult;
617619
u32 tx_hwtstamp_timeouts;
@@ -1108,6 +1110,8 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index);
11081110
void i40e_ptp_set_increment(struct i40e_pf *pf);
11091111
int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr);
11101112
int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr);
1113+
void i40e_ptp_save_hw_time(struct i40e_pf *pf);
1114+
void i40e_ptp_restore_hw_time(struct i40e_pf *pf);
11111115
void i40e_ptp_init(struct i40e_pf *pf);
11121116
void i40e_ptp_stop(struct i40e_pf *pf);
11131117
int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi);

drivers/net/ethernet/intel/i40e/i40e_main.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9301,6 +9301,11 @@ static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired)
93019301
dev_warn(&pf->pdev->dev,
93029302
"shutdown_lan_hmc failed: %d\n", ret);
93039303
}
9304+
9305+
/* Save the current PTP time so that we can restore the time after the
9306+
* reset completes.
9307+
*/
9308+
i40e_ptp_save_hw_time(pf);
93049309
}
93059310

93069311
/**

drivers/net/ethernet/intel/i40e/i40e_ptp.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,16 +725,68 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
725725
pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
726726
pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
727727

728+
/* Set the previous "reset" time to the current Kernel clock time */
729+
pf->ptp_prev_hw_time = ktime_to_timespec64(ktime_get_real());
730+
pf->ptp_reset_start = ktime_get();
731+
728732
return 0;
729733
}
730734

735+
/**
736+
* i40e_ptp_save_hw_time - Save the current PTP time as ptp_prev_hw_time
737+
* @pf: Board private structure
738+
*
739+
* Read the current PTP time and save it into pf->ptp_prev_hw_time. This should
740+
* be called at the end of preparing to reset, just before hardware reset
741+
* occurs, in order to preserve the PTP time as close as possible across
742+
* resets.
743+
*/
744+
void i40e_ptp_save_hw_time(struct i40e_pf *pf)
745+
{
746+
/* don't try to access the PTP clock if it's not enabled */
747+
if (!(pf->flags & I40E_FLAG_PTP))
748+
return;
749+
750+
i40e_ptp_gettimex(&pf->ptp_caps, &pf->ptp_prev_hw_time, NULL);
751+
/* Get a monotonic starting time for this reset */
752+
pf->ptp_reset_start = ktime_get();
753+
}
754+
755+
/**
756+
* i40e_ptp_restore_hw_time - Restore the ptp_prev_hw_time + delta to PTP regs
757+
* @pf: Board private structure
758+
*
759+
* Restore the PTP hardware clock registers. We previously cached the PTP
760+
* hardware time as pf->ptp_prev_hw_time. To be as accurate as possible,
761+
* update this value based on the time delta since the time was saved, using
762+
* CLOCK_MONOTONIC (via ktime_get()) to calculate the time difference.
763+
*
764+
* This ensures that the hardware clock is restored to nearly what it should
765+
* have been if a reset had not occurred.
766+
*/
767+
void i40e_ptp_restore_hw_time(struct i40e_pf *pf)
768+
{
769+
ktime_t delta = ktime_sub(ktime_get(), pf->ptp_reset_start);
770+
771+
/* Update the previous HW time with the ktime delta */
772+
timespec64_add_ns(&pf->ptp_prev_hw_time, ktime_to_ns(delta));
773+
774+
/* Restore the hardware clock registers */
775+
i40e_ptp_settime(&pf->ptp_caps, &pf->ptp_prev_hw_time);
776+
}
777+
731778
/**
732779
* i40e_ptp_init - Initialize the 1588 support after device probe or reset
733780
* @pf: Board private structure
734781
*
735782
* This function sets device up for 1588 support. The first time it is run, it
736783
* will create a PHC clock device. It does not create a clock device if one
737784
* already exists. It also reconfigures the device after a reset.
785+
*
786+
* The first time a clock is created, i40e_ptp_create_clock will set
787+
* pf->ptp_prev_hw_time to the current system time. During resets, it is
788+
* expected that this timespec will be set to the last known PTP clock time,
789+
* in order to preserve the clock time as close as possible across a reset.
738790
**/
739791
void i40e_ptp_init(struct i40e_pf *pf)
740792
{
@@ -766,7 +818,6 @@ void i40e_ptp_init(struct i40e_pf *pf)
766818
dev_err(&pf->pdev->dev, "%s: ptp_clock_register failed\n",
767819
__func__);
768820
} else if (pf->ptp_clock) {
769-
struct timespec64 ts;
770821
u32 regval;
771822

772823
if (pf->hw.debug_mask & I40E_DEBUG_LAN)
@@ -787,9 +838,8 @@ void i40e_ptp_init(struct i40e_pf *pf)
787838
/* reset timestamping mode */
788839
i40e_ptp_set_timestamp_mode(pf, &pf->tstamp_config);
789840

790-
/* Set the clock value. */
791-
ts = ktime_to_timespec64(ktime_get_real());
792-
i40e_ptp_settime(&pf->ptp_caps, &ts);
841+
/* Restore the clock time based on last known value */
842+
i40e_ptp_restore_hw_time(pf);
793843
}
794844
}
795845

0 commit comments

Comments
 (0)