| From ee96ded43f6b6119b210e05f2a63ac83c60a9503 Mon Sep 17 00:00:00 2001 |
| From: "Alex Maftei (amaftei)" <amaftei@solarflare.com> |
| Date: Wed, 26 Feb 2020 17:33:19 +0000 |
| Subject: [PATCH] sfc: fix timestamp reconstruction at 16-bit rollover points |
| |
| commit 23797b98909f34b75fd130369bde86f760db69d0 upstream. |
| |
| 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c |
| index af15a737c675..59b4f16896a8 100644 |
| --- a/drivers/net/ethernet/sfc/ptp.c |
| +++ b/drivers/net/ethernet/sfc/ptp.c |
| @@ -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); |
| -- |
| 2.7.4 |
| |