mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v3 0/3] sfc: fix PTP synchronization races
@ 2026-10-21  9:15 Alex Austin
  2026-10-21  9:15 ` [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start() Alex Austin
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Alex Austin @ 2026-10-21  9:15 UTC (permalink / raw)
  To: Edward Cree, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Richard Cochran, netdev, linux-net-drivers, linux-kernel, Alex Austin

This series fixes PTP synchronization races found by code inspection, and
a related frequency-adjustment bug found while reviewing the locking.

efx_ptp_start() resets the driver's cached current_adjfreq as though
MC_CMD_PTP_OP_ENABLE also resets the NIC's frequency adjustment on the
MC. It does not, so the next time-only adjustment reprograms the clock
from a stale cache. Patch 1 removes this reset.

efx_ptp_pps_worker() and the SIOCSHWTSTAMP path through
efx_ptp_change_mode() both call efx_ptp_synchronize(). Concurrent calls
can interfere with the shared DMA handshake and the shared timeset
results, because the MCDI interface is released inside
efx_mcdi_rpc_finish() and acquired after the handshake flag is cleared.
Patch 2 serializes a function's synchronization transactions and clock
operations under a new mutex. Patch 3 returns the PPS timestamp in
caller-owned storage to prevent concurrent synchronization from
overwriting it while the PPS worker reads it.

Tested on two hosts with X2522-25G NICs cabled back to back at 25G.
Both PCI functions share one clock.

Clock operations on the function that registers the clock were run
against repeated efx_ptp_change_mode() -> efx_ptp_synchronize() on the
other function:

  hwstamp_ctl -i ens41f0np0 -r 1 -t 1
  hwstamp_ctl -i ens41f1np1 -r 1 -t 1

  for i in $(seq 150); do
          phc_ctl /dev/ptp1 freq 1000
          phc_ctl /dev/ptp1 adj 0.0001
          phc_ctl /dev/ptp1 get
  done &
  for i in $(seq 40); do
          hwstamp_ctl -i ens41f1np1 -r 0 -t 0
          hwstamp_ctl -i ens41f1np1 -r 1 -t 1
  done &
  wait

  ethtool -S ens41f1np1 | grep ptp_

                                without    with
  ptp_good_syncs                     400     400
  ptp_bad_syncs                        0       0
  ptp_sync_timeouts                    0       0
  ptp_no_time_syncs                    0       0
  ptp_invalid_sync_windows             0       0
  ptp_undersize_sync_windows           0       8
  ptp_oversize_sync_windows            0       0

The undersize counter rejects readings as implausibly fast rather than
inaccurate. FW reports a 40ns minimum sync window; without the series
the fastest readings sit exactly on that limit, and with it they sit a
few nanoseconds below. The shift tracks the barrier in the mutex
acquisition rather than the serialization it provides: an smp_mb() alone
where the mutex is taken reproduces the counter on an otherwise
unmodified driver, while a plain memory access there does not. No
synchronization failed: each takes eight readings and needs one to
succeed.

End-to-end PTP sync quality is unchanged (tested with ptp4l and
phc2sys).

Changes since v2:
 - add patch 1, dropping the stale current_adjfreq reset in
   efx_ptp_start() found while re-reviewing the locking this series
   adds.
Changes since v1:
 - document in efx_ptp_synchronize() why a secondary function needs no
   cross-function locking.
 - take phc_lock in efx_ptp_start() around the current_adjfreq reset
 - do not take phc_lock in efx_phc_gettime(): it reads no shared state
   and MCDI already serializes the command, so _efx_phc_gettime() is
   gone and efx_phc_settime() calls efx_phc_gettime() under the lock
 - keep the comment above efx_phc_settime() (Edward Cree)
 - add the test results above
 - return the PPS timestamp through the caller's own pps_event_time
   rather than adding a struct.

Alex Austin (3):
  sfc: ptp: drop the stale frequency reset in efx_ptp_start()
  sfc: ptp: serialize host and MC synchronization transactions
  sfc: ptp: avoid racing PPS timestamp updates

 drivers/net/ethernet/sfc/ptp.c | 108 ++++++++++++++++++++++-----------
 1 file changed, 71 insertions(+), 37 deletions(-)

-- 
2.34.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start()
  2026-10-21  9:15 [PATCH net v3 0/3] sfc: fix PTP synchronization races Alex Austin
@ 2026-10-21  9:15 ` Alex Austin
  2026-10-25 11:12   ` netdev-bot+sashiko
  2026-10-21  9:15 ` [PATCH net v3 2/3] sfc: ptp: serialize host and MC synchronization transactions Alex Austin
  2026-10-21  9:15 ` [PATCH net v3 3/3] sfc: ptp: avoid racing PPS timestamp updates Alex Austin
  2 siblings, 1 reply; 6+ messages in thread
From: Alex Austin @ 2026-10-21  9:15 UTC (permalink / raw)
  To: Edward Cree, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Richard Cochran, netdev, linux-net-drivers, linux-kernel, Alex Austin

FW does not touch the clock frequency state on MC_CMD_PTP_OP_DISABLE or
MC_CMD_PTP_OP_ENABLE, so the driver doesn't need to either. Doing so
just desyncs the driver's cached current_adjfreq from what FW still has
programmed.

efx_ptp_start() is also reached from reset recovery, but that is fine:
on an FLR or PCIe reset FW does not touch the NIC clock, and on a full
MC reboot the clock is reset to 1970, so a stale current_adjfreq from
before the reset is hardly consequential - especially as it is bound to
MAX_PPB, 1000000 ppb.

Fixes: ffe9554c17bf ("sfc: Add support for IEEE-1588 PTP")

Signed-off-by: Alex Austin <alex.austin@amd.com>
---
 drivers/net/ethernet/sfc/ptp.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index 6ca9a75..2247e5e 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -1446,7 +1446,6 @@ static int efx_ptp_start(struct efx_nic *efx)
 		goto fail;
 
 	ptp->evt_frag_idx = 0;
-	ptp->current_adjfreq = 0;
 
 	return 0;
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net v3 2/3] sfc: ptp: serialize host and MC synchronization transactions
  2026-10-21  9:15 [PATCH net v3 0/3] sfc: fix PTP synchronization races Alex Austin
  2026-10-21  9:15 ` [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start() Alex Austin
@ 2026-10-21  9:15 ` Alex Austin
  2026-10-25 11:12   ` netdev-bot+sashiko
  2026-10-21  9:15 ` [PATCH net v3 3/3] sfc: ptp: avoid racing PPS timestamp updates Alex Austin
  2 siblings, 1 reply; 6+ messages in thread
From: Alex Austin @ 2026-10-21  9:15 UTC (permalink / raw)
  To: Edward Cree, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Richard Cochran, netdev, linux-net-drivers, linux-kernel,
	Alex Austin, Alejandro Lucero, Pieter Jansen van Vuuren

efx_ptp_synchronize() clears the MC-ready DMA flag before
efx_mcdi_rpc_start() acquires the MCDI interface, and processes results
into ptp->timeset[] after efx_mcdi_rpc_finish() has released it. The PPS
worker and the hwtstamp enable path can therefore clobber each other's
handshake flag and timesets.

Hold a PHC mutex across the whole transaction and over the clock
operations, so efx_phc_settime() reads and adjusts atomically and
current_adjfreq is consistently read and written under one lock. Also
bail out when efx_mcdi_rpc_start() fails rather than finishing an RPC
that was never acquired.

Fixes: 7c236c43b838 ("sfc: Add support for IEEE-1588 PTP")
Signed-off-by: Alex Austin <alex.austin@amd.com>
Reviewed-by: Alejandro Lucero <alucerop@amd.com>
Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Acked-by: Edward Cree <ecree.xilinx@gmail.com>
---
 drivers/net/ethernet/sfc/ptp.c | 81 +++++++++++++++++++++++-----------
 1 file changed, 55 insertions(+), 26 deletions(-)

diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index 2247e5e..eedab53 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -36,6 +36,7 @@
 #include <linux/errno.h>
 #include <linux/ktime.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/pps_kernel.h>
 #include <linux/ptp_clock_kernel.h>
 #include "net_driver.h"
@@ -272,6 +273,8 @@ struct efx_ptp_rxfilter {
  * frequency adjustment into a fixed point fractional nanosecond format.
  * @current_adjfreq: Current ppb adjustment.
  * @phc_clock: Pointer to registered phc device (if primary function)
+ * @phc_lock: Serializes PHC commands and synchronization, and protects
+ *	      @current_adjfreq
  * @phc_clock_info: Registration structure for phc device
  * @pps_work: pps work task for handling pps events
  * @pps_workwq: pps work queue
@@ -331,6 +334,7 @@ struct efx_ptp_data {
 	unsigned int adjfreq_ppb_shift;
 	s64 current_adjfreq;
 	struct ptp_clock *phc_clock;
+	struct mutex phc_lock;
 	struct ptp_clock_info phc_clock_info;
 	struct work_struct pps_work;
 	struct workqueue_struct *pps_workwq;
@@ -1008,7 +1012,14 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
 	return 0;
 }
 
-/* Synchronize times between the host and the MC */
+/* Synchronize times between the host and the MC.
+ *
+ * The MC serializes PTP requests, so MC_CMD_PTP_OP_ADJUST cannot be applied
+ * while MC_CMD_PTP_OP_SYNCHRONIZE is collecting timesets. This holds across
+ * functions of a controller, which share the clock but adjust it only through
+ * the function that registered it, so a secondary function can capture here
+ * without serializing against the primary.
+ */
 static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
 {
 	struct efx_ptp_data *ptp = efx->ptp_data;
@@ -1027,11 +1038,14 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
 	MCDI_SET_QWORD(synch_buf, PTP_IN_SYNCHRONIZE_START_ADDR,
 		       ptp->start.dma_addr);
 
+	mutex_lock(&ptp->phc_lock);
+
 	/* Clear flag that signals MC ready */
 	WRITE_ONCE(*start, 0);
 	rc = efx_mcdi_rpc_start(efx, MC_CMD_PTP, synch_buf,
 				MC_CMD_PTP_IN_SYNCHRONIZE_LEN);
-	EFX_WARN_ON_ONCE_PARANOID(rc);
+	if (rc)
+		goto out;
 
 	/* Wait for start from MCDI (or timeout) */
 	timeout = jiffies + msecs_to_jiffies(MAX_SYNCHRONISE_WAIT_MS);
@@ -1062,11 +1076,13 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
 			++ptp->no_time_syncs;
 	}
 
+out:
 	/* Increment the bad syncs counter if the synchronize fails, whatever
 	 * the reason.
 	 */
 	if (rc != 0)
 		++ptp->bad_syncs;
+	mutex_unlock(&ptp->phc_lock);
 
 	return rc;
 }
@@ -1569,6 +1585,7 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
 	if (!efx->ptp_data)
 		return -ENOMEM;
 
+	mutex_init(&ptp->phc_lock);
 	ptp->efx = efx;
 	ptp->channel = channel;
 
@@ -1640,6 +1657,7 @@ fail2:
 	efx_nic_free_buffer(efx, &ptp->start);
 
 fail1:
+	mutex_destroy(&ptp->phc_lock);
 	kfree(efx->ptp_data);
 	efx->ptp_data = NULL;
 
@@ -1694,6 +1712,7 @@ void efx_ptp_remove(struct efx_nic *efx)
 	destroy_workqueue(efx->ptp_data->workwq);
 
 	efx_nic_free_buffer(efx, &efx->ptp_data->start);
+	mutex_destroy(&efx->ptp_data->phc_lock);
 	kfree(efx->ptp_data);
 	efx->ptp_data = NULL;
 }
@@ -2105,23 +2124,23 @@ static int efx_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 	MCDI_SET_QWORD(inadj, PTP_IN_ADJUST_FREQ, adjustment_ns);
 	MCDI_SET_DWORD(inadj, PTP_IN_ADJUST_SECONDS, 0);
 	MCDI_SET_DWORD(inadj, PTP_IN_ADJUST_NANOSECONDS, 0);
+	mutex_lock(&ptp_data->phc_lock);
 	rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inadj, sizeof(inadj),
 			  NULL, 0, NULL);
-	if (rc != 0)
-		return rc;
-
-	ptp_data->current_adjfreq = adjustment_ns;
-	return 0;
+	if (!rc)
+		ptp_data->current_adjfreq = adjustment_ns;
+	mutex_unlock(&ptp_data->phc_lock);
+	return rc;
 }
 
-static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
+static int _efx_phc_adjtime(struct efx_ptp_data *ptp_data, s64 delta)
+	__must_hold(&ptp_data->phc_lock)
 {
-	u32 nic_major, nic_minor;
-	struct efx_ptp_data *ptp_data = container_of(ptp,
-						     struct efx_ptp_data,
-						     phc_clock_info);
-	struct efx_nic *efx = ptp_data->efx;
 	MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_ADJUST_LEN);
+	struct efx_nic *efx = ptp_data->efx;
+	u32 nic_major, nic_minor;
+
+	lockdep_assert_held(&ptp_data->phc_lock);
 
 	efx->ptp_data->ns_to_nic_time(delta, &nic_major, &nic_minor);
 
@@ -2134,6 +2153,18 @@ static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
 			    NULL, 0, NULL);
 }
 
+static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct efx_ptp_data *ptp_data = container_of(ptp, struct efx_ptp_data,
+						  phc_clock_info);
+	int rc;
+
+	mutex_lock(&ptp_data->phc_lock);
+	rc = _efx_phc_adjtime(ptp_data, delta);
+	mutex_unlock(&ptp_data->phc_lock);
+	return rc;
+}
+
 static int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
 {
 	struct efx_ptp_data *ptp_data = container_of(ptp,
@@ -2165,23 +2196,21 @@ static int efx_phc_settime(struct ptp_clock_info *ptp,
 {
 	/* Get the current NIC time, efx_phc_gettime.
 	 * Subtract from the desired time to get the offset
-	 * call efx_phc_adjtime with the offset
+	 * call _efx_phc_adjtime with the offset
 	 */
+	struct efx_ptp_data *ptp_data = container_of(ptp, struct efx_ptp_data,
+						  phc_clock_info);
+	struct timespec64 time_now, delta;
 	int rc;
-	struct timespec64 time_now;
-	struct timespec64 delta;
 
+	mutex_lock(&ptp_data->phc_lock);
 	rc = efx_phc_gettime(ptp, &time_now);
-	if (rc != 0)
-		return rc;
-
-	delta = timespec64_sub(*e_ts, time_now);
-
-	rc = efx_phc_adjtime(ptp, timespec64_to_ns(&delta));
-	if (rc != 0)
-		return rc;
-
-	return 0;
+	if (!rc) {
+		delta = timespec64_sub(*e_ts, time_now);
+		rc = _efx_phc_adjtime(ptp_data, timespec64_to_ns(&delta));
+	}
+	mutex_unlock(&ptp_data->phc_lock);
+	return rc;
 }
 
 static int efx_phc_enable(struct ptp_clock_info *ptp,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net v3 3/3] sfc: ptp: avoid racing PPS timestamp updates
  2026-10-21  9:15 [PATCH net v3 0/3] sfc: fix PTP synchronization races Alex Austin
  2026-10-21  9:15 ` [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start() Alex Austin
  2026-10-21  9:15 ` [PATCH net v3 2/3] sfc: ptp: serialize host and MC synchronization transactions Alex Austin
@ 2026-10-21  9:15 ` Alex Austin
  2 siblings, 0 replies; 6+ messages in thread
From: Alex Austin @ 2026-10-21  9:15 UTC (permalink / raw)
  To: Edward Cree, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Richard Cochran, netdev, linux-net-drivers, linux-kernel,
	Alex Austin, Alejandro Lucero, Pieter Jansen van Vuuren

efx_ptp_pps_worker() can race with SIOCHWTSTAMP via
efx_ptp_change_mode(). After the worker completes synchronization, the
ioctl can start another synchronization and update host_time_pps while
the worker copies it into the PPS event. This can report a partially
updated timestamp. Serializing synchronization transactions alone does
not protect this copy, which happens after phc_lock is released.

Return the timestamp through the caller's own pps_event_time rather
than the shared field in efx_ptp_data, so the worker reads storage no
one else can write.

Fixes: 7c236c43b838 ("sfc: Add support for IEEE-1588 PTP")
Signed-off-by: Alex Austin <alex.austin@amd.com>
Reviewed-by: Alejandro Lucero <alucerop@amd.com>
Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
---
 drivers/net/ethernet/sfc/ptp.c | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index eedab53..3c6a85b 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -268,7 +268,6 @@ struct efx_ptp_rxfilter {
  * @evt_frag_idx: Current fragment number
  * @evt_code: Last event code
  * @start: Address at which MC indicates ready for synchronisation
- * @host_time_pps: Host time at last PPS
  * @adjfreq_ppb_shift: Shift required to convert scaled parts-per-billion
  * frequency adjustment into a fixed point fractional nanosecond format.
  * @current_adjfreq: Current ppb adjustment.
@@ -330,7 +329,6 @@ struct efx_ptp_data {
 	int evt_frag_idx;
 	int evt_code;
 	struct efx_buffer start;
-	struct pps_event_time host_time_pps;
 	unsigned int adjfreq_ppb_shift;
 	s64 current_adjfreq;
 	struct ptp_clock *phc_clock;
@@ -912,7 +910,8 @@ static void efx_ptp_read_timeset(MCDI_DECLARE_STRUCT_PTR(data),
 static int
 efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
 		      size_t response_length,
-		      const struct pps_event_time *last_time)
+		      const struct pps_event_time *last_time,
+		      struct pps_event_time *host_time_pps)
 {
 	unsigned number_readings =
 		MCDI_VAR_ARRAY_LEN(response_length,
@@ -990,6 +989,10 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
 			   "PTP bad synchronisation seconds\n");
 		return -EAGAIN;
 	}
+
+	if (!host_time_pps)
+		return 0;
+
 	delta.tv_sec = (last_sec - start_sec) & 1;
 	delta.tv_nsec =
 		last_time->ts_real.tv_nsec -
@@ -1006,8 +1009,8 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
 	delta.tv_nsec += ktime_to_timespec64(mc_time).tv_nsec;
 
 	/* Set PPS timestamp to match NIC top of second */
-	ptp->host_time_pps = *last_time;
-	pps_sub_ts(&ptp->host_time_pps, delta);
+	*host_time_pps = *last_time;
+	pps_sub_ts(host_time_pps, delta);
 
 	return 0;
 }
@@ -1020,7 +1023,8 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
  * the function that registered it, so a secondary function can capture here
  * without serializing against the primary.
  */
-static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
+static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings,
+			       struct pps_event_time *host_time_pps)
 {
 	struct efx_ptp_data *ptp = efx->ptp_data;
 	MCDI_DECLARE_BUF(synch_buf, MC_CMD_PTP_OUT_SYNCHRONIZE_LENMAX);
@@ -1069,7 +1073,7 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
 				 &response_length);
 	if (rc == 0) {
 		rc = efx_ptp_process_times(efx, synch_buf, response_length,
-					   &last_time);
+					   &last_time, host_time_pps);
 		if (rc == 0)
 			++ptp->good_syncs;
 		else
@@ -1501,14 +1505,15 @@ static void efx_ptp_pps_worker(struct work_struct *work)
 {
 	struct efx_ptp_data *ptp =
 		container_of(work, struct efx_ptp_data, pps_work);
+	struct pps_event_time host_time_pps = {};
 	struct efx_nic *efx = ptp->efx;
 	struct ptp_clock_event ptp_evt;
 
-	if (efx_ptp_synchronize(efx, PTP_SYNC_ATTEMPTS))
+	if (efx_ptp_synchronize(efx, PTP_SYNC_ATTEMPTS, &host_time_pps))
 		return;
 
 	ptp_evt.type = PTP_CLOCK_PPSUSR;
-	ptp_evt.pps_times = ptp->host_time_pps;
+	ptp_evt.pps_times = host_time_pps;
 	ptp_clock_event(ptp->phc_clock, &ptp_evt);
 }
 
@@ -1844,7 +1849,8 @@ int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
 				rc = efx_ptp_start(efx);
 			if (rc == 0) {
 				rc = efx_ptp_synchronize(efx,
-							 PTP_SYNC_ATTEMPTS * 2);
+							 PTP_SYNC_ATTEMPTS * 2,
+							 NULL);
 				if (rc != 0)
 					efx_ptp_stop(efx);
 			}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start()
  2026-10-21  9:15 ` [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start() Alex Austin
@ 2026-10-25 11:12   ` netdev-bot+sashiko
  0 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-10-25 11:12 UTC (permalink / raw)
  To: alex.austin
  Cc: ecree.xilinx, andrew+netdev, davem, edumazet, kuba, pabeni,
	richardcochran, netdev, linux-net-drivers, linux-kernel,
	alucerop, pieter.jansen-van-vuuren

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 0 (1)

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The fix leaves the forked Siena driver unfixed.
- [Medium] The commit message understates the bug it fixes and has an
  invalid Fixes: tag.

Pre-existing issues:
- [Low] This problem wasn't introduced by this patch, but the kernel-doc
  for struct efx_ptp_data describes `@current_adjfreq: Current ppb…

--

sfc: ptp: drop the stale frequency reset in efx_ptp_start()

This patch removes the line that resets ptp->current_adjfreq to 0 in
efx_ptp_start() in drivers/net/ethernet/sfc/ptp.c. According to the commit
message, firmware keeps the clock frequency across MC_CMD_PTP_OP_DISABLE
and MC_CMD_PTP_OP_ENABLE. Zeroing the cached value therefore puts the driver
out of sync with the firmware.

> FW does not touch the clock frequency state on MC_CMD_PTP_OP_DISABLE or
> MC_CMD_PTP_OP_ENABLE, so the driver doesn't need to either. Doing so
> just desyncs the driver's cached current_adjfreq from what FW still has
> programmed.

[Severity: Medium]
Could the commit message describe the effect users see? efx_phc_adjtime()
sends the cached value to firmware on every time step:

drivers/net/ethernet/sfc/ptp.c:efx_phc_adjtime() {
    ...
	MCDI_SET_QWORD(inbuf, PTP_IN_ADJUST_FREQ, ptp_data->current_adjfreq);
    ...
}

So before this patch, the first clock step after any efx_ptp_start() set
the NIC frequency to 0. That covers a SIOCSHWTSTAMP mode change, ifup, and
the reset_required path in efx_ptp_worker().

The servo's frequency correction was lost. Meanwhile, ptp_clock_adjtime()
kept reporting the old dialed_frequency to userspace.

That is more than a cached value going out of sync. Stating it would help
backporters judge how serious the fix is.

> Fixes: ffe9554c17bf ("sfc: Add support for IEEE-1588 PTP")

Is this hash right? ffe9554c17bf does not resolve to any commit. The commit
with this subject appears to be 7c236c43b838, which the other patches in
this series also use:

Fixes: 7c236c43b838 ("sfc: Add support for IEEE-1588 PTP")

> diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
> index 6ca9a75af01d8..2247e5e3e25a8 100644
> --- a/drivers/net/ethernet/sfc/ptp.c
> +++ b/drivers/net/ethernet/sfc/ptp.c
> @@ -1446,7 +1446,6 @@ static int efx_ptp_start(struct efx_nic *efx)
>  		goto fail;
>  
>  	ptp->evt_frag_idx = 0;
> -	ptp->current_adjfreq = 0;

[Severity: Medium]
Does the Siena driver need the same change? Its copy of efx_ptp_start()
still has the same assignment:

drivers/net/ethernet/sfc/siena/ptp.c:efx_ptp_start() {
    ...
	ptp->evt_frag_idx = 0;
	ptp->current_adjfreq = 0;
    ...
}

Siena reaches efx_ptp_start() through the same paths:

- efx_siena_ptp_change_mode() on SIOCSHWTSTAMP
- efx_ptp_restart(), called from efx_siena_ptp_start_datapath() on ifup
  and reset recovery
- the reset_required branch of efx_ptp_worker()

Siena's efx_phc_adjtime() then sends the zeroed value to firmware as
PTP_IN_ADJUST_FREQ in MC_CMD_PTP_OP_ADJUST.

The commit message does not say whether the firmware behaviour on
MC_CMD_PTP_OP_DISABLE and MC_CMD_PTP_OP_ENABLE also applies to Siena
firmware.

If it does, can the same desync still happen in sfc-siena?

If it does not, is this change wrong for stable trees from before the
sfc/siena split? In those trees, drivers/net/ethernet/sfc/ptp.c also drives
Siena NICs. Also, 7c236c43b838 added efx_ptp_probe() to siena_probe_nic(),
so this code was first written for Siena firmware.

>  
>  	return 0;
>  

[Severity: Low]
This isn't a bug introduced by this patch, but the kernel-doc for struct
efx_ptp_data in drivers/net/ethernet/sfc/ptp.c says:

 * @current_adjfreq: Current ppb adjustment.

Is this unit correct? efx_phc_adjfine() stores adjustment_ns in this field,
not a ppb value:

drivers/net/ethernet/sfc/ptp.c:efx_phc_adjfine() {
    ...
	ptp_data->current_adjfreq = adjustment_ns;
    ...
}

adjustment_ns is the ppb delta after conversion, using adjfreq_ppb_shift,
into the NIC's fixed point fractional nanosecond format. The same comment
is in drivers/net/ethernet/sfc/siena/ptp.c.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261021091534.3164258-1-alex.austin%40amd.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net v3 2/3] sfc: ptp: serialize host and MC synchronization transactions
  2026-10-21  9:15 ` [PATCH net v3 2/3] sfc: ptp: serialize host and MC synchronization transactions Alex Austin
@ 2026-10-25 11:12   ` netdev-bot+sashiko
  0 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-10-25 11:12 UTC (permalink / raw)
  To: alex.austin
  Cc: ecree.xilinx, andrew+netdev, davem, edumazet, kuba, pabeni,
	richardcochran, netdev, linux-net-drivers, linux-kernel,
	alucerop, pieter.jansen-van-vuuren

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 (1) · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The Siena driver keeps its own copy of the PTP code in
  drivers/net/ethernet/sfc/siena/ptp.c (built into sfc-siena by…
- [Low] The new kernel-doc for @phc_lock in struct efx_ptp_data
  (drivers/net/ethernet/sfc/ptp.c line 276) says the lock "Serializes…

Pre-existing issues:
- [Medium] At this commit, efx_ptp_pps_worker()
  (drivers/net/ethernet/sfc/ptp.c line 1511) copies the shared…

--

sfc: ptp: serialize host and MC synchronization transactions

This adds a phc_lock mutex to struct efx_ptp_data. The lock is held for
the whole efx_ptp_synchronize() transaction and for the PHC adjfine,
adjtime and settime operations. It also makes efx_ptp_synchronize()
bail out when efx_mcdi_rpc_start() fails, instead of finishing an RPC
that was never started.

> diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
> index 2247e5e3e25a8..eedab53e308af 100644
> --- a/drivers/net/ethernet/sfc/ptp.c
> +++ b/drivers/net/ethernet/sfc/ptp.c

[ ... ]

> @@ -272,6 +273,8 @@ struct efx_ptp_rxfilter {
>   * frequency adjustment into a fixed point fractional nanosecond format.
>   * @current_adjfreq: Current ppb adjustment.
>   * @phc_clock: Pointer to registered phc device (if primary function)
> + * @phc_lock: Serializes PHC commands and synchronization, and protects
> + *	      @current_adjfreq

[Severity: Low]
This isn't a bug, but does this comment claim more than the code does?
efx_phc_gettime(), the .gettime64 op, sends MC_CMD_PTP_OP_READ_NIC_TIME
without taking phc_lock. Only efx_phc_settime() holds the lock when it
calls efx_phc_gettime():

efx_phc_gettime() {
    ...
	rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
			  outbuf, sizeof(outbuf), NULL);
    ...
}

efx_phc_enable() also changes nic_ts_enabled without the lock. Only
adjfine, adjtime, settime and synchronize are serialized.

Could the comment name the operations the lock actually covers? Or
should gettime take the lock as well?

>   * @phc_clock_info: Registration structure for phc device
>   * @pps_work: pps work task for handling pps events
>   * @pps_workwq: pps work queue

[ ... ]

> @@ -1027,11 +1038,14 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
>  	MCDI_SET_QWORD(synch_buf, PTP_IN_SYNCHRONIZE_START_ADDR,
>  		       ptp->start.dma_addr);
>  
> +	mutex_lock(&ptp->phc_lock);
> +
>  	/* Clear flag that signals MC ready */
>  	WRITE_ONCE(*start, 0);
>  	rc = efx_mcdi_rpc_start(efx, MC_CMD_PTP, synch_buf,
>  				MC_CMD_PTP_IN_SYNCHRONIZE_LEN);
> -	EFX_WARN_ON_ONCE_PARANOID(rc);
> +	if (rc)
> +		goto out;

[Severity: Medium]
Does the Siena driver need the same fix?

drivers/net/ethernet/sfc/siena/ptp.c is a separate copy that is built
into sfc-siena (siena/Makefile lists ptp.o). Its efx_ptp_synchronize()
still matches the version this patch replaces:

drivers/net/ethernet/sfc/siena/ptp.c:efx_ptp_synchronize() {
    ...
	WRITE_ONCE(*start, 0);
	rc = efx_siena_mcdi_rpc_start(efx, MC_CMD_PTP, synch_buf,
				      MC_CMD_PTP_IN_SYNCHRONIZE_LEN);
	EFX_WARN_ON_ONCE_PARANOID(rc);
    ...
}

No lock is held there. efx_ptp_pps_worker() and
efx_siena_ptp_change_mode() can still race. The second one is reached
from ptp_set_ts_config() in siena.c. Each can clear the other's
handshake flag and overwrite ptp->timeset[] and ptp->host_time_pps.

efx_siena_mcdi_rpc_start() can return -ENETDOWN early, before
efx_mcdi_acquire_sync(), when mc_bist_for_other_fn is set or the mode
is MCDI_MODE_FAIL. The Siena function still calls
efx_siena_mcdi_rpc_finish() in that case, so it waits on and releases
an MCDI interface it never acquired.

Siena's efx_phc_settime() still calls efx_phc_gettime() and then
efx_phc_adjtime() as two separate steps, with no lock held.

current_adjfreq is still written without a lock in efx_phc_adjfine()
and efx_ptp_start(), and read in efx_phc_adjtime(). Patch 1 of this
series removes the efx_ptp_start() reset only from sfc. Nothing under
sfc/siena/ has a phc_lock.

The later "sfc: ptp: avoid racing PPS timestamp updates" patch fixes a
PPS race that is also still present in Siena:

drivers/net/ethernet/sfc/siena/ptp.c:efx_ptp_pps_worker() {
    ...
	ptp_evt.pps_times = ptp->host_time_pps;
    ...
}

All of this code descends from 7c236c43b838, the commit named in the
Fixes: tag. Should there be a companion sfc/siena patch, or at least a
note in the commit message, so that stable backports cover Siena too?

>  
>  	/* Wait for start from MCDI (or timeout) */
>  	timeout = jiffies + msecs_to_jiffies(MAX_SYNCHRONISE_WAIT_MS);

[ ... ]

> @@ -1062,11 +1076,13 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
>  			++ptp->no_time_syncs;
>  	}
>  
> +out:
>  	/* Increment the bad syncs counter if the synchronize fails, whatever
>  	 * the reason.
>  	 */
>  	if (rc != 0)
>  		++ptp->bad_syncs;
> +	mutex_unlock(&ptp->phc_lock);

[Severity: Medium]
This is a pre-existing issue and was not introduced by this patch, but
ptp->host_time_pps is still read after phc_lock has been dropped.
efx_ptp_pps_worker() copies it after efx_ptp_synchronize() returns:

efx_ptp_pps_worker() {
    ...
	ptp_evt.pps_times = ptp->host_time_pps;
    ...
}

At the same time,
efx_ptp_change_mode()->efx_ptp_synchronize()->efx_ptp_process_times()
can be rewriting the field in two steps under the lock:

	ptp->host_time_pps = *last_time;
	pps_sub_ts(&ptp->host_time_pps, delta);

Can the worker then pass a half-updated PPS timestamp, or the other
caller's timestamp, to ptp_clock_event()?

The next patch in the series, "sfc: ptp: avoid racing PPS timestamp
updates", appears to fix this for sfc. It returns the timestamp through
a caller-owned struct pps_event_time in efx_ptp_pps_worker() and passes
NULL from efx_ptp_change_mode().

>  
>  	return rc;
>  }

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261021091534.3164258-1-alex.austin%40amd.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-25 11:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-21  9:15 [PATCH net v3 0/3] sfc: fix PTP synchronization races Alex Austin
2026-10-21  9:15 ` [PATCH net v3 1/3] sfc: ptp: drop the stale frequency reset in efx_ptp_start() Alex Austin
2026-10-25 11:12   ` netdev-bot+sashiko
2026-10-21  9:15 ` [PATCH net v3 2/3] sfc: ptp: serialize host and MC synchronization transactions Alex Austin
2026-10-25 11:12   ` netdev-bot+sashiko
2026-10-21  9:15 ` [PATCH net v3 3/3] sfc: ptp: avoid racing PPS timestamp updates Alex Austin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®