Skip to content

Commit

Permalink
sfc: fix timestamp reconstruction at 16-bit rollover points
Browse files Browse the repository at this point in the history
We can't just use the top bits of the last sync event as they could be
off-by-one every 65,536 seconds, giving an error in reconstruction of
65,536 seconds.

This patch uses the difference in the bottom 16 bits (mod 2^16) to
calculate an offset that needs to be applied to the last sync event to
get to the current time.

Signed-off-by: Alexandru-Mihai Maftei <amaftei@solarflare.com>
Acked-by: Martin Habets <mhabets@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Alex Maftei (amaftei) authored and David S. Miller committed Feb 27, 2020
1 parent 3f74957 commit 23797b9
Showing 1 changed file with 35 additions and 3 deletions.
38 changes: 35 additions & 3 deletions drivers/net/ethernet/sfc/ptp.c
Original file line number Diff line number Diff line change
@@ -560,13 +560,45 @@ efx_ptp_mac_nic_to_ktime_correction(struct efx_nic *efx,
u32 nic_major, u32 nic_minor,
s32 correction)
{
u32 sync_timestamp;
ktime_t kt = { 0 };
s16 delta;

if (!(nic_major & 0x80000000)) {
WARN_ON_ONCE(nic_major >> 16);
/* Use the top bits from the latest sync event. */
nic_major &= 0xffff;
nic_major |= (last_sync_timestamp_major(efx) & 0xffff0000);

/* Medford provides 48 bits of timestamp, so we must get the top
* 16 bits from the timesync event state.
*
* We only have the lower 16 bits of the time now, but we do
* have a full resolution timestamp at some point in past. As
* long as the difference between the (real) now and the sync
* is less than 2^15, then we can reconstruct the difference
* between those two numbers using only the lower 16 bits of
* each.
*
* Put another way
*
* a - b = ((a mod k) - b) mod k
*
* when -k/2 < (a-b) < k/2. In our case k is 2^16. We know
* (a mod k) and b, so can calculate the delta, a - b.
*
*/
sync_timestamp = last_sync_timestamp_major(efx);

/* Because delta is s16 this does an implicit mask down to
* 16 bits which is what we need, assuming
* MEDFORD_TX_SECS_EVENT_BITS is 16. delta is signed so that
* we can deal with the (unlikely) case of sync timestamps
* arriving from the future.
*/
delta = nic_major - sync_timestamp;

/* Recover the fully specified time now, by applying the offset
* to the (fully specified) sync time.
*/
nic_major = sync_timestamp + delta;

kt = ptp->nic_to_kernel_time(nic_major, nic_minor,
correction);

0 comments on commit 23797b9

Please sign in to comment.