Netdev List
 help / color / mirror / Atom feed
* [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races
@ 2026-08-05  6:51 xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 1/5] i40e: serialize Tx timestamp skb ownership xuanqiang.luo
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: xuanqiang.luo @ 2026-08-05  6:51 UTC (permalink / raw)
  To: intel-wired-lan, horms
  Cc: vadim.fedorenko, anthony.l.nguyen, przemyslaw.kitszel,
	andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
	piotr.kwapulinski, arkadiusz.kubalewski, aleksandr.loktionov,
	netdev, Xuanqiang Luo

From: Xuanqiang Luo <xuanqiang.luo@linux.dev>

The i40e PTP paths have independent ownership and lifetime races. A Tx
timestamp skb can be released concurrently, timestamp configuration can
race PTP teardown, reset recovery can rebuild the device during removal,
and external timestamp work can outlive the PHC or PF.

Split the fixes by invariant so each patch remains independently
reviewable. The series is ordered so the external timestamp work fix can
rely on the configuration and reset teardown synchronization introduced
earlier.

---
Changes:
v2:
  Patch 1 (new):
  - Split Tx timestamp skb ownership into an independent patch and protect
    the slot across transmit, timeout, interrupt, error, and stop paths.
    (Simon Horman, Sashiko)
  - Use ptp_tx_skb as the single source of slot state and verify the skb
    owner before releasing it from the Tx error path.
  - Keep the existing enable checks outside ptp_tx_lock so the critical
    section only covers pending slot ownership.
  Patch 2 (new):
  - Serialize the PTP_ENA transition with hardware timestamp configuration
    so an ioctl cannot re-enable interrupts during teardown. (Sashiko)
  Patch 3 (new):
  - Split reset/recovery synchronization from the external timestamp work
    fix and stop service work before PTP and device teardown.
  Patch 4 (new):
  - Move the reset polling replacement into its own patch and use the
    wait-bit API for reset ownership. (Simon Horman, Sashiko)
  Patch 5:
  - Close the configuration gate, then disable external timestamp events
    and drain the work outside ptp_config_lock before unregistering the
    PHC.

v1: https://lore.kernel.org/all/20260724093448.63154-1-xuanqiang.luo@linux.dev/

Xuanqiang Luo (5):
  i40e: serialize Tx timestamp skb ownership
  i40e: serialize timestamp configuration with PTP teardown
  i40e: synchronize reset recovery with device removal
  i40e: replace reset polling with wait-bit synchronization
  i40e: fix races in PTP external timestamp work handling

 drivers/net/ethernet/intel/i40e/i40e.h      |  6 +-
 drivers/net/ethernet/intel/i40e/i40e_main.c | 37 ++++----
 drivers/net/ethernet/intel/i40e/i40e_ptp.c  | 95 ++++++++++++++-------
 drivers/net/ethernet/intel/i40e/i40e_txrx.c | 38 ++++++---
 4 files changed, 120 insertions(+), 56 deletions(-)


base-commit: 075b74841bd0065a3bda3440873c747938e69b68
-- 
2.43.0

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

* [PATCH iwl-net v2 1/5] i40e: serialize Tx timestamp skb ownership
  2026-08-05  6:51 [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races xuanqiang.luo
@ 2026-08-05  6:51 ` xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 2/5] i40e: serialize timestamp configuration with PTP teardown xuanqiang.luo
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: xuanqiang.luo @ 2026-08-05  6:51 UTC (permalink / raw)
  To: intel-wired-lan, horms
  Cc: vadim.fedorenko, anthony.l.nguyen, przemyslaw.kitszel,
	andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
	piotr.kwapulinski, arkadiusz.kubalewski, aleksandr.loktionov,
	netdev, Xuanqiang Luo

From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>

The Tx timestamp interrupt, watchdog, transmit error path, and PTP stop
path can all release ptp_tx_skb. The existing in-progress bit serializes
slot reservation, but does not protect the skb pointer. One path can
therefore free the skb while another is still using it, causing a
use-after-free or double free.

Add ptp_tx_lock to protect the timestamp slot. With all accesses
serialized, use a non-NULL ptp_tx_skb as the single indication that the
slot is occupied and remove the redundant in-progress bit. Publish and
claim the skb under the lock, then complete or free it after unlocking.

Make the transmit error path claim the slot only when it still points to
the skb whose transmission failed. This prevents delayed error cleanup
from releasing a newer timestamp request after another path recycled the
slot.

Fixes: 0bc0706b46cd ("i40e: check for Tx timestamp timeouts during watchdog")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |  3 +-
 drivers/net/ethernet/intel/i40e/i40e_main.c |  1 +
 drivers/net/ethernet/intel/i40e/i40e_ptp.c  | 55 ++++++++++++++-------
 drivers/net/ethernet/intel/i40e/i40e_txrx.c | 38 ++++++++++----
 4 files changed, 67 insertions(+), 30 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 83e780919ac97..4c8bcaa9dd192 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -101,7 +101,6 @@ enum i40e_state {
 	__I40E_GLOBAL_RESET_REQUESTED,
 	__I40E_EMP_RESET_INTR_RECEIVED,
 	__I40E_SUSPENDED,
-	__I40E_PTP_TX_IN_PROGRESS,
 	__I40E_BAD_EEPROM,
 	__I40E_DOWN_REQUESTED,
 	__I40E_FD_FLUSH_REQUESTED,
@@ -675,6 +674,8 @@ struct i40e_pf {
 	u32 tx_hwtstamp_skipped;
 	u32 rx_hwtstamp_cleared;
 	u32 latch_event_flags;
+	/* Protects ptp_tx_skb, which also indicates the slot state. */
+	spinlock_t ptp_tx_lock;
 	spinlock_t ptp_rx_lock; /* Used to protect Rx timestamp registers. */
 	unsigned long latch_events[4];
 	bool ptp_tx;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index a04683004a567..0a259351c127e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12819,6 +12819,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
 			 "total-port-shutdown was enabled, link-down-on-close is forced on\n");
 	}
 	mutex_init(&pf->switch_mutex);
+	spin_lock_init(&pf->ptp_tx_lock);
 
 sw_init_done:
 	return err;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index ff62b5f2c8150..224568a3af085 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -728,14 +728,17 @@ void i40e_ptp_rx_hang(struct i40e_pf *pf)
  **/
 void i40e_ptp_tx_hang(struct i40e_pf *pf)
 {
+	unsigned long flags;
 	struct sk_buff *skb;
 
 	if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags) || !pf->ptp_tx)
 		return;
 
+	spin_lock_irqsave(&pf->ptp_tx_lock, flags);
+
 	/* Nothing to do if we're not already waiting for a timestamp */
-	if (!test_bit(__I40E_PTP_TX_IN_PROGRESS, pf->state))
-		return;
+	if (!pf->ptp_tx_skb)
+		goto out_unlock;
 
 	/* We already have a handler routine which is run when we are notified
 	 * of a Tx timestamp in the hardware. If we don't get an interrupt
@@ -744,12 +747,16 @@ void i40e_ptp_tx_hang(struct i40e_pf *pf)
 	if (time_is_before_jiffies(pf->ptp_tx_start + HZ)) {
 		skb = pf->ptp_tx_skb;
 		pf->ptp_tx_skb = NULL;
-		clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
+		spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 
-		/* Free the skb after we clear the bitlock */
+		/* Free the skb after releasing the lock. */
 		dev_kfree_skb_any(skb);
 		pf->tx_hwtstamp_timeouts++;
+		return;
 	}
+
+out_unlock:
+	spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 }
 
 /**
@@ -763,17 +770,21 @@ void i40e_ptp_tx_hang(struct i40e_pf *pf)
 void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf)
 {
 	struct skb_shared_hwtstamps shhwtstamps;
-	struct sk_buff *skb = pf->ptp_tx_skb;
 	struct i40e_hw *hw = &pf->hw;
+	unsigned long flags;
+	struct sk_buff *skb;
 	u32 hi, lo;
 	u64 ns;
 
 	if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags) || !pf->ptp_tx)
 		return;
 
-	/* don't attempt to timestamp if we don't have an skb */
-	if (!pf->ptp_tx_skb)
-		return;
+	spin_lock_irqsave(&pf->ptp_tx_lock, flags);
+
+	/* Take ownership of the skb before another cleanup path can free it. */
+	skb = pf->ptp_tx_skb;
+	if (!skb)
+		goto out_unlock;
 
 	lo = rd32(hw, I40E_PRTTSYN_TXTIME_L);
 	hi = rd32(hw, I40E_PRTTSYN_TXTIME_H);
@@ -781,17 +792,21 @@ void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf)
 	ns = (((u64)hi) << 32) | lo;
 	i40e_ptp_convert_to_hwtstamp(&shhwtstamps, ns);
 
-	/* Clear the bit lock as soon as possible after reading the register,
-	 * and prior to notifying the stack via skb_tstamp_tx(). Otherwise
-	 * applications might wake up and attempt to request another transmit
-	 * timestamp prior to the bit lock being cleared.
+	/* Mark the slot available as soon as possible after reading the
+	 * register and before notifying the stack via skb_tstamp_tx().
+	 * Otherwise, applications might wake up and request another transmit
+	 * timestamp before the slot is available.
 	 */
 	pf->ptp_tx_skb = NULL;
-	clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
+	spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 
 	/* Notify the stack and free the skb after we've unlocked */
 	skb_tstamp_tx(skb, &shhwtstamps);
 	dev_kfree_skb_any(skb);
+	return;
+
+out_unlock:
+	spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 }
 
 /**
@@ -1539,19 +1554,21 @@ void i40e_ptp_stop(struct i40e_pf *pf)
 {
 	struct i40e_vsi *main_vsi = i40e_pf_get_main_vsi(pf);
 	struct i40e_hw *hw = &pf->hw;
+	unsigned long flags;
+	struct sk_buff *skb;
 	u32 regval;
 
+	spin_lock_irqsave(&pf->ptp_tx_lock, flags);
 	clear_bit(I40E_FLAG_PTP_ENA, pf->flags);
 	pf->ptp_tx = false;
-	pf->ptp_rx = false;
+	skb = pf->ptp_tx_skb;
+	pf->ptp_tx_skb = NULL;
+	spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 
-	if (pf->ptp_tx_skb) {
-		struct sk_buff *skb = pf->ptp_tx_skb;
+	pf->ptp_rx = false;
 
-		pf->ptp_tx_skb = NULL;
-		clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
+	if (skb)
 		dev_kfree_skb_any(skb);
-	}
 
 	if (pf->ptp_clock) {
 		ptp_clock_unregister(pf->ptp_clock);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 894f2d06d39d0..f430546acf422 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -3198,7 +3198,9 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len,
 static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb,
 		     u32 tx_flags, u64 *cd_type_cmd_tso_mss)
 {
+	unsigned long flags;
 	struct i40e_pf *pf;
+	int tsyn = 0;
 
 	if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)))
 		return 0;
@@ -3211,23 +3213,28 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb,
 	 * we are not already transmitting a packet to be timestamped
 	 */
 	pf = i40e_netdev_to_pf(tx_ring->netdev);
+	spin_lock_irqsave(&pf->ptp_tx_lock, flags);
+
 	if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags))
-		return 0;
+		goto out_unlock;
 
-	if (pf->ptp_tx &&
-	    !test_and_set_bit_lock(__I40E_PTP_TX_IN_PROGRESS, pf->state)) {
+	if (pf->ptp_tx && !pf->ptp_tx_skb) {
 		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 		pf->ptp_tx_start = jiffies;
 		pf->ptp_tx_skb = skb_get(skb);
+		tsyn = 1;
 	} else {
 		pf->tx_hwtstamp_skipped++;
-		return 0;
 	}
 
-	*cd_type_cmd_tso_mss |= (u64)I40E_TX_CTX_DESC_TSYN <<
-				I40E_TXD_CTX_QW1_CMD_SHIFT;
+out_unlock:
+	spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 
-	return 1;
+	if (tsyn)
+		*cd_type_cmd_tso_mss |= (u64)I40E_TX_CTX_DESC_TSYN <<
+					I40E_TXD_CTX_QW1_CMD_SHIFT;
+
+	return tsyn;
 }
 
 /**
@@ -3957,10 +3964,21 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
 cleanup_tx_tstamp:
 	if (unlikely(tx_flags & I40E_TX_FLAGS_TSYN)) {
 		struct i40e_pf *pf = i40e_netdev_to_pf(tx_ring->netdev);
+		struct sk_buff *ptp_skb = NULL;
+		unsigned long flags;
+
+		spin_lock_irqsave(&pf->ptp_tx_lock, flags);
+		/* Another cleanup path may have handed the slot
+		 * to a new skb.
+		 */
+		if (pf->ptp_tx_skb == skb) {
+			ptp_skb = pf->ptp_tx_skb;
+			pf->ptp_tx_skb = NULL;
+		}
+		spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 
-		dev_kfree_skb_any(pf->ptp_tx_skb);
-		pf->ptp_tx_skb = NULL;
-		clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
+		if (ptp_skb)
+			dev_kfree_skb_any(ptp_skb);
 	}
 
 	return NETDEV_TX_OK;
-- 
2.43.0


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

* [PATCH iwl-net v2 2/5] i40e: serialize timestamp configuration with PTP teardown
  2026-08-05  6:51 [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 1/5] i40e: serialize Tx timestamp skb ownership xuanqiang.luo
@ 2026-08-05  6:51 ` xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 3/5] i40e: synchronize reset recovery with device removal xuanqiang.luo
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: xuanqiang.luo @ 2026-08-05  6:51 UTC (permalink / raw)
  To: intel-wired-lan, horms
  Cc: vadim.fedorenko, anthony.l.nguyen, przemyslaw.kitszel,
	andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
	piotr.kwapulinski, arkadiusz.kubalewski, aleksandr.loktionov,
	netdev, Xuanqiang Luo

From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>

i40e_remove() can call i40e_ptp_stop() without the networking locks held.
A concurrent hardware timestamp request can observe I40E_FLAG_PTP_ENA
before teardown clears it, then program PRTTSYN_CTL0 after teardown has
disabled the event source. This can leave time sync interrupts enabled
during device removal.

Add ptp_config_lock to serialize the enable check and register programming
in i40e_ptp_hwtstamp_set() with the PTP state transition in
i40e_ptp_stop(). Requests which observe PTP enabled finish programming
before the state transition; later requests return -EOPNOTSUPP and cannot
re-enable the interrupt.

Fixes: 1050713026a0 ("i40e: add support for PTP external synchronization clock")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |  2 ++
 drivers/net/ethernet/intel/i40e/i40e_main.c |  1 +
 drivers/net/ethernet/intel/i40e/i40e_ptp.c  | 17 +++++++++++++----
 3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 4c8bcaa9dd192..84564d747d09a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -668,6 +668,8 @@ struct i40e_pf {
 	struct timespec64 ptp_prev_hw_time;
 	struct work_struct ptp_extts0_work;
 	ktime_t ptp_reset_start;
+	/* Serializes timestamp configuration with PTP teardown. */
+	struct mutex ptp_config_lock;
 	struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */
 	u32 ptp_adj_mult;
 	u32 tx_hwtstamp_timeouts;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 0a259351c127e..8c28788ac634f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12819,6 +12819,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
 			 "total-port-shutdown was enabled, link-down-on-close is forced on\n");
 	}
 	mutex_init(&pf->switch_mutex);
+	mutex_init(&pf->ptp_config_lock);
 	spin_lock_init(&pf->ptp_tx_lock);
 
 sw_init_done:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 224568a3af085..f194869947124 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -1328,17 +1328,23 @@ int i40e_ptp_hwtstamp_set(struct net_device *netdev,
 	struct i40e_pf *pf = np->vsi->back;
 	int err;
 
-	if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags))
-		return -EOPNOTSUPP;
+	mutex_lock(&pf->ptp_config_lock);
+
+	if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags)) {
+		err = -EOPNOTSUPP;
+		goto unlock;
+	}
 
 	err = i40e_ptp_set_timestamp_mode(pf, config);
 	if (err)
-		return err;
+		goto unlock;
 
 	/* save these settings for future reference */
 	pf->tstamp_config = *config;
 
-	return 0;
+unlock:
+	mutex_unlock(&pf->ptp_config_lock);
+	return err;
 }
 
 /**
@@ -1558,6 +1564,8 @@ void i40e_ptp_stop(struct i40e_pf *pf)
 	struct sk_buff *skb;
 	u32 regval;
 
+	mutex_lock(&pf->ptp_config_lock);
+
 	spin_lock_irqsave(&pf->ptp_tx_lock, flags);
 	clear_bit(I40E_FLAG_PTP_ENA, pf->flags);
 	pf->ptp_tx = false;
@@ -1566,6 +1574,7 @@ void i40e_ptp_stop(struct i40e_pf *pf)
 	spin_unlock_irqrestore(&pf->ptp_tx_lock, flags);
 
 	pf->ptp_rx = false;
+	mutex_unlock(&pf->ptp_config_lock);
 
 	if (skb)
 		dev_kfree_skb_any(skb);
-- 
2.43.0


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

* [PATCH iwl-net v2 3/5] i40e: synchronize reset recovery with device removal
  2026-08-05  6:51 [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 1/5] i40e: serialize Tx timestamp skb ownership xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 2/5] i40e: serialize timestamp configuration with PTP teardown xuanqiang.luo
@ 2026-08-05  6:51 ` xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 4/5] i40e: replace reset polling with wait-bit synchronization xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 5/5] i40e: fix races in PTP external timestamp work handling xuanqiang.luo
  4 siblings, 0 replies; 6+ messages in thread
From: xuanqiang.luo @ 2026-08-05  6:51 UTC (permalink / raw)
  To: intel-wired-lan, horms
  Cc: vadim.fedorenko, anthony.l.nguyen, przemyslaw.kitszel,
	andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
	piotr.kwapulinski, arkadiusz.kubalewski, aleksandr.loktionov,
	netdev, Xuanqiang Luo

From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>

i40e_remove() currently tears down PTP and device resources before
claiming RESET_RECOVERY_PENDING. A service task can pass its initial state
check before removal starts and continue into reset recovery after the
remove path has begun teardown. This can make i40e_reset_and_rebuild()
access or recreate resources that are being released.

Stop the service timer and disable the service work before claiming reset
ownership. disable_work_sync() drains the current service task and rejects
later queue_work() attempts, including those from interrupts. Claim the
reset bit before starting teardown so an existing reset owner completes
first.

Fixes: 6533e558c650 ("i40e: Fix reset path while removing the driver")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
 drivers/net/ethernet/intel/i40e/i40e_main.c | 28 ++++++++++-----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 8c28788ac634f..5922f83f324ae 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -16159,6 +16159,20 @@ static void i40e_remove(struct pci_dev *pdev)
 
 	i40e_devlink_unregister(pf);
 
+	/* Interrupts can queue the service task until __I40E_DOWN is set.
+	 * Disable it so no service work can outlive the PF.
+	 */
+	set_bit(__I40E_SUSPENDED, pf->state);
+	if (pf->service_timer.function)
+		timer_shutdown_sync(&pf->service_timer);
+	if (pf->service_task.func)
+		disable_work_sync(&pf->service_task);
+
+	/* Wait for any reset owner before tearing down device resources. */
+	while (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
+		usleep_range(1000, 2000);
+	set_bit(__I40E_IN_REMOVE, pf->state);
+
 	i40e_dbg_pf_exit(pf);
 
 	i40e_ptp_stop(pf);
@@ -16167,26 +16181,12 @@ static void i40e_remove(struct pci_dev *pdev)
 	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), 0);
 	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), 0);
 
-	/* Grab __I40E_RESET_RECOVERY_PENDING and set __I40E_IN_REMOVE
-	 * flags, once they are set, i40e_rebuild should not be called as
-	 * i40e_prep_for_reset always returns early.
-	 */
-	while (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
-		usleep_range(1000, 2000);
-	set_bit(__I40E_IN_REMOVE, pf->state);
-
 	if (test_bit(I40E_FLAG_SRIOV_ENA, pf->flags)) {
 		set_bit(__I40E_VF_RESETS_DISABLED, pf->state);
 		i40e_free_vfs(pf);
 		clear_bit(I40E_FLAG_SRIOV_ENA, pf->flags);
 	}
-	/* no more scheduling of any task */
-	set_bit(__I40E_SUSPENDED, pf->state);
 	set_bit(__I40E_DOWN, pf->state);
-	if (pf->service_timer.function)
-		timer_shutdown_sync(&pf->service_timer);
-	if (pf->service_task.func)
-		cancel_work_sync(&pf->service_task);
 
 	if (test_bit(__I40E_RECOVERY_MODE, pf->state)) {
 		struct i40e_vsi *vsi = pf->vsi[0];
-- 
2.43.0


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

* [PATCH iwl-net v2 4/5] i40e: replace reset polling with wait-bit synchronization
  2026-08-05  6:51 [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races xuanqiang.luo
                   ` (2 preceding siblings ...)
  2026-08-05  6:51 ` [PATCH iwl-net v2 3/5] i40e: synchronize reset recovery with device removal xuanqiang.luo
@ 2026-08-05  6:51 ` xuanqiang.luo
  2026-08-05  6:51 ` [PATCH iwl-net v2 5/5] i40e: fix races in PTP external timestamp work handling xuanqiang.luo
  4 siblings, 0 replies; 6+ messages in thread
From: xuanqiang.luo @ 2026-08-05  6:51 UTC (permalink / raw)
  To: intel-wired-lan, horms
  Cc: vadim.fedorenko, anthony.l.nguyen, przemyslaw.kitszel,
	andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
	piotr.kwapulinski, arkadiusz.kubalewski, aleksandr.loktionov,
	netdev, Xuanqiang Luo

From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>

i40e_remove() polls on RESET_RECOVERY_PENDING with a fixed sleep while
waiting for an existing reset owner. This adds teardown latency and does
not provide a wait queue for the reset owner to wake.

Use wait_on_bit_lock() so removal sleeps until the reset owner releases the
bit and then atomically claims it. Pair the reset release paths with
clear_and_wake_up_bit() to provide the required release and wakeup
semantics.

Fixes: 5b6d4a7f20b0 ("i40e: Fix crash during removing i40e driver")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
 drivers/net/ethernet/intel/i40e/i40e_main.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 5922f83f324ae..c19d4c81d6532 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -7,6 +7,7 @@
 #include <linux/if_bridge.h>
 #include <linux/if_macvlan.h>
 #include <linux/module.h>
+#include <linux/wait_bit.h>
 #include <net/pkt_cls.h>
 #include <net/xdp_sock_drv.h>
 
@@ -10822,7 +10823,8 @@ static int i40e_reset(struct i40e_pf *pf)
 	if (ret) {
 		dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret);
 		set_bit(__I40E_RESET_FAILED, pf->state);
-		clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state);
+		clear_and_wake_up_bit(__I40E_RESET_RECOVERY_PENDING,
+				      pf->state);
 	} else {
 		pf->pfr_count++;
 	}
@@ -11115,7 +11117,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
 end_core_reset:
 	clear_bit(__I40E_RESET_FAILED, pf->state);
 clear_recovery:
-	clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state);
+	clear_and_wake_up_bit(__I40E_RESET_RECOVERY_PENDING, pf->state);
 	clear_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state);
 }
 
@@ -16169,8 +16171,8 @@ static void i40e_remove(struct pci_dev *pdev)
 		disable_work_sync(&pf->service_task);
 
 	/* Wait for any reset owner before tearing down device resources. */
-	while (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
-		usleep_range(1000, 2000);
+	wait_on_bit_lock(pf->state, __I40E_RESET_RECOVERY_PENDING,
+			 TASK_UNINTERRUPTIBLE);
 	set_bit(__I40E_IN_REMOVE, pf->state);
 
 	i40e_dbg_pf_exit(pf);
-- 
2.43.0


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

* [PATCH iwl-net v2 5/5] i40e: fix races in PTP external timestamp work handling
  2026-08-05  6:51 [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races xuanqiang.luo
                   ` (3 preceding siblings ...)
  2026-08-05  6:51 ` [PATCH iwl-net v2 4/5] i40e: replace reset polling with wait-bit synchronization xuanqiang.luo
@ 2026-08-05  6:51 ` xuanqiang.luo
  4 siblings, 0 replies; 6+ messages in thread
From: xuanqiang.luo @ 2026-08-05  6:51 UTC (permalink / raw)
  To: intel-wired-lan, horms
  Cc: vadim.fedorenko, anthony.l.nguyen, przemyslaw.kitszel,
	andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
	piotr.kwapulinski, arkadiusz.kubalewski, aleksandr.loktionov,
	netdev, Xuanqiang Luo

From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>

The time sync interrupt queues ptp_extts0_work, which reads device
registers and reports events through pf->ptp_clock. i40e_ptp_stop()
unregisters the PHC before disabling the event source or draining the
work. The worker can therefore use the clock or PF after either has been
freed.

Move the event-source shutdown before PHC unregister. i40e_ptp_stop()
first clears I40E_FLAG_PTP_ENA under ptp_config_lock, so timestamp
configuration cannot re-enable the source after the lock is released.
Then call disable_work_sync() to reject later queue attempts and drain
work already pending or running.

INIT_WORK() is currently called from i40e_ptp_set_timestamp_mode(), which
runs on reset and whenever timestamping is reconfigured. Reinitializing
pending or running work can corrupt its workqueue state. Initialize the
work once from i40e_sw_init() instead.

Fixes: 1050713026a0 ("i40e: add support for PTP external synchronization clock")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |  1 +
 drivers/net/ethernet/intel/i40e/i40e_main.c |  1 +
 drivers/net/ethernet/intel/i40e/i40e_ptp.c  | 25 +++++++++++++++------
 3 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 84564d747d09a..18452f9aa74e1 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -1318,6 +1318,7 @@ int i40e_ptp_hwtstamp_set(struct net_device *netdev,
 			  struct netlink_ext_ack *extack);
 void i40e_ptp_save_hw_time(struct i40e_pf *pf);
 void i40e_ptp_restore_hw_time(struct i40e_pf *pf);
+void i40e_ptp_init_work(struct i40e_pf *pf);
 void i40e_ptp_init(struct i40e_pf *pf);
 void i40e_ptp_stop(struct i40e_pf *pf);
 int i40e_ptp_alloc_pins(struct i40e_pf *pf);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index c19d4c81d6532..48ba38a452d0b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12822,6 +12822,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
 	}
 	mutex_init(&pf->switch_mutex);
 	mutex_init(&pf->ptp_config_lock);
+	i40e_ptp_init_work(pf);
 	spin_lock_init(&pf->ptp_tx_lock);
 
 sw_init_done:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index f194869947124..5f15a3d4a91ed 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -169,6 +169,17 @@ static void i40e_ptp_extts0_work(struct work_struct *work)
 	ptp_clock_event(pf->ptp_clock, &event);
 }
 
+/**
+ * i40e_ptp_init_work - Initialize PTP work for a PF
+ * @pf: Board private structure
+ *
+ * Initialize work which must remain valid for the lifetime of the PF.
+ */
+void i40e_ptp_init_work(struct i40e_pf *pf)
+{
+	INIT_WORK(&pf->ptp_extts0_work, i40e_ptp_extts0_work);
+}
+
 /**
  * i40e_is_ptp_pin_dev - check if device supports PTP pins
  * @hw: pointer to the hardware structure
@@ -1201,8 +1212,6 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf,
 	regval |= 1 << I40E_PRTTSYN_CTL0_EVENT_INT_ENA_SHIFT;
 	wr32(hw, I40E_PRTTSYN_CTL0, regval);
 
-	INIT_WORK(&pf->ptp_extts0_work, i40e_ptp_extts0_work);
-
 	switch (config->tx_type) {
 	case HWTSTAMP_TX_OFF:
 		pf->ptp_tx = false;
@@ -1576,6 +1585,13 @@ void i40e_ptp_stop(struct i40e_pf *pf)
 	pf->ptp_rx = false;
 	mutex_unlock(&pf->ptp_config_lock);
 
+	/* Stop external timestamp events before unregistering the clock. */
+	regval = rd32(hw, I40E_PRTTSYN_CTL0);
+	regval &= ~I40E_PRTTSYN_CTL0_EVENT_INT_ENA_MASK;
+	wr32(hw, I40E_PRTTSYN_CTL0, regval);
+
+	disable_work_sync(&pf->ptp_extts0_work);
+
 	if (skb)
 		dev_kfree_skb_any(skb);
 
@@ -1596,10 +1612,5 @@ void i40e_ptp_stop(struct i40e_pf *pf)
 	regval &= ~I40E_PRTTSYN_AUX_0_PTPFLAG_MASK;
 	wr32(hw, I40E_PRTTSYN_AUX_0(0), regval);
 
-	/* Disable interrupts */
-	regval = rd32(hw, I40E_PRTTSYN_CTL0);
-	regval &= ~I40E_PRTTSYN_CTL0_EVENT_INT_ENA_MASK;
-	wr32(hw, I40E_PRTTSYN_CTL0, regval);
-
 	i40e_ptp_free_pins(pf);
 }
-- 
2.43.0


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

end of thread, other threads:[~2026-08-05  6:53 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05  6:51 [PATCH iwl-net v2 0/5] i40e: fix PTP work and teardown races xuanqiang.luo
2026-08-05  6:51 ` [PATCH iwl-net v2 1/5] i40e: serialize Tx timestamp skb ownership xuanqiang.luo
2026-08-05  6:51 ` [PATCH iwl-net v2 2/5] i40e: serialize timestamp configuration with PTP teardown xuanqiang.luo
2026-08-05  6:51 ` [PATCH iwl-net v2 3/5] i40e: synchronize reset recovery with device removal xuanqiang.luo
2026-08-05  6:51 ` [PATCH iwl-net v2 4/5] i40e: replace reset polling with wait-bit synchronization xuanqiang.luo
2026-08-05  6:51 ` [PATCH iwl-net v2 5/5] i40e: fix races in PTP external timestamp work handling xuanqiang.luo

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