Skip to content

Commit d29a813

Browse files
michichanguy11
authored andcommitted
ice: avoid the PTP hardware semaphore in gettimex64 path
The PTP hardware semaphore (PFTSYN_SEM) is used to synchronize operations that program the PTP timers. The operations involve issuing commands to the sideband queue. The E810 does not have a hardware sideband queue, so the admin queue is used. The admin queue is slow. I have observed delays in hundreds of milliseconds waiting for ice_sq_done. When phc2sys reads the time from the ice PTP clock and PFTSYN_SEM is held by a task performing one of the slow operations, ice_ptp_lock can easily time out. phc2sys gets -EBUSY and the kernel prints: ice 0000:XX:YY.0: PTP failed to get time These messages appear once every few seconds, causing log spam. The E810 datasheet recommends an algorithm for reading the upper 64 bits of the GLTSYN_TIME register. It matches what's implemented in ice_ptp_read_src_clk_reg. It is robust against wrap-around, but not necessarily against the concurrent setting of the register (with GLTSYN_CMD_{INIT,ADJ}_TIME commands). Perhaps that's why ice_ptp_gettimex64 also takes PFTSYN_SEM. The race with time setters can be prevented without relying on the PTP hardware semaphore. Using the "ice_adapter" from the previous patch, we can have a common spinlock for the PFs that share the clock hardware. It will protect the reading and writing to the GLTSYN_TIME register. The writing is performed indirectly, by the hardware, as a result of the driver writing GLTSYN_CMD_SYNC in ice_ptp_exec_tmr_cmd. I wasn't sure if the ice_flush there is enough to make sure GLTSYN_TIME has been updated, but it works well in my testing. My test code can be seen here: https://gitlab.com/mschmidt2/linux/-/commits/ice-ptp-host-side-lock-10 It consists of: - kernel threads reading the time in a busy loop and looking at the deltas between consecutive values, reporting new maxima. - a shell script that sets the time repeatedly; - a bpftrace probe to produce a histogram of the measured deltas. Without the spinlock ptp_gltsyn_time_lock, it is easy to see tearing. Deltas in the [2G, 4G) range appear in the histograms. With the spinlock added, there is no tearing and the biggest delta I saw was in the range [1M, 2M), that is under 2 ms. Reviewed-by: Jacob Keller <[email protected]> Reviewed-by: Przemek Kitszel <[email protected]> Signed-off-by: Michal Schmidt <[email protected]> Tested-by: Pucha Himasekhar Reddy <[email protected]> (A Contingent worker at Intel) Signed-off-by: Tony Nguyen <[email protected]>
1 parent 0e2bddf commit d29a813

File tree

4 files changed

+12
-7
lines changed

4 files changed

+12
-7
lines changed

drivers/net/ethernet/intel/ice/ice_adapter.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/mutex.h>
77
#include <linux/pci.h>
88
#include <linux/slab.h>
9+
#include <linux/spinlock.h>
910
#include <linux/xarray.h>
1011
#include "ice_adapter.h"
1112

@@ -35,6 +36,7 @@ static struct ice_adapter *ice_adapter_new(void)
3536
if (!adapter)
3637
return NULL;
3738

39+
spin_lock_init(&adapter->ptp_gltsyn_time_lock);
3840
refcount_set(&adapter->refcount, 1);
3941

4042
return adapter;

drivers/net/ethernet/intel/ice/ice_adapter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@
44
#ifndef _ICE_ADAPTER_H_
55
#define _ICE_ADAPTER_H_
66

7+
#include <linux/spinlock_types.h>
78
#include <linux/refcount_types.h>
89

910
struct pci_dev;
1011

1112
/**
1213
* struct ice_adapter - PCI adapter resources shared across PFs
14+
* @ptp_gltsyn_time_lock: Spinlock protecting access to the GLTSYN_TIME
15+
* register of the PTP clock.
1316
* @refcount: Reference count. struct ice_pf objects hold the references.
1417
*/
1518
struct ice_adapter {
19+
/* For access to the GLTSYN_TIME register */
20+
spinlock_t ptp_gltsyn_time_lock;
21+
1622
refcount_t refcount;
1723
};
1824

drivers/net/ethernet/intel/ice/ice_ptp.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ ice_ptp_read_src_clk_reg(struct ice_pf *pf, struct ptp_system_timestamp *sts)
374374
u8 tmr_idx;
375375

376376
tmr_idx = ice_get_ptp_src_clock_index(hw);
377+
guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock);
377378
/* Read the system timestamp pre PHC read */
378379
ptp_read_system_prets(sts);
379380

@@ -1925,15 +1926,8 @@ ice_ptp_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts,
19251926
struct ptp_system_timestamp *sts)
19261927
{
19271928
struct ice_pf *pf = ptp_info_to_pf(info);
1928-
struct ice_hw *hw = &pf->hw;
1929-
1930-
if (!ice_ptp_lock(hw)) {
1931-
dev_err(ice_pf_to_dev(pf), "PTP failed to get time\n");
1932-
return -EBUSY;
1933-
}
19341929

19351930
ice_ptp_read_time(pf, ts, sts);
1936-
ice_ptp_unlock(hw);
19371931

19381932
return 0;
19391933
}

drivers/net/ethernet/intel/ice/ice_ptp_hw.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
274274
*/
275275
static void ice_ptp_exec_tmr_cmd(struct ice_hw *hw)
276276
{
277+
struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
278+
279+
guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock);
277280
wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD);
278281
ice_flush(hw);
279282
}

0 commit comments

Comments
 (0)