[PATCH net] net: fec: drain PEROUT requests before canceling the timer
From: mhun512
Date: Thu Sep 24 2026 - 22:04:51 EST
fec_ptp_stop() cancels perout_timer before unregistering the PTP clock.
A PEROUT ioctl can set perout_enable, release the driver's locks and
then be preempted before hrtimer_start(). The unregister path waits for
that ioctl, which can rearm the timer after it was canceled. Removal
then frees the netdev containing the still-armed timer.
Unregister the PTP clock first so all in-flight ioctls have returned
before canceling the timer. Clear the PEROUT state and compare channel
afterward, then disable PPS. Serialize the channel write with PTP clock
shutdown and skip it when that clock is off.
This issue was identified during our ongoing static-analysis research
while reviewing kernel code.
Fixes: 350749b909bf ("net: fec: Add support for periodic output signal of PPS")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Co-developed-by: Ijae Kim <ae878000@xxxxxxxxx>
Signed-off-by: Ijae Kim <ae878000@xxxxxxxxx>
Signed-off-by: Myeonghun Pak <mhun512@xxxxxxxxx>
---
drivers/net/ethernet/freescale/fec_ptp.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c
b/drivers/net/ethernet/freescale/fec_ptp.c
index 56801c2009d5..67901e036ac3 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -506,10 +506,13 @@ static int fec_ptp_pps_disable(struct
fec_enet_private *fep, uint channel)
hrtimer_cancel(&fep->perout_timer);
+ mutex_lock(&fep->ptp_clk_mutex);
spin_lock_irqsave(&fep->tmreg_lock, flags);
fep->perout_enable = false;
- writel(0, fep->hwp + FEC_TCSR(channel));
+ if (fep->ptp_clk_on)
+ writel(0, fep->hwp + FEC_TCSR(channel));
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+ mutex_unlock(&fep->ptp_clk_mutex);
return 0;
}
@@ -856,11 +859,12 @@ void fec_ptp_stop(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
- if (fep->pps_enable)
- fec_ptp_enable_pps(fep, 0);
-
cancel_delayed_work_sync(&fep->time_keep);
- hrtimer_cancel(&fep->perout_timer);
if (fep->ptp_clock)
ptp_clock_unregister(fep->ptp_clock);
+
+ /* An in-flight PEROUT ioctl can arm the timer until unregister returns. */
+ fec_ptp_pps_disable(fep, fep->pps_channel);
+ if (fep->pps_enable)
+ fec_ptp_enable_pps(fep, 0);
}
--
2.53.0