sfc: fix timestamp reconstruction at 16-bit rollover points
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>
This commit is contained in:
parent
3f74957fcb
commit
23797b9890
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue