* [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
* 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
* [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
* 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
* [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
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®