The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] net: hip04: quiesce tx coalesce timer before teardown
@ 2026-07-03  5:01 Fan Wu
  2026-07-09 12:43 ` Simon Horman
  0 siblings, 1 reply; 3+ messages in thread
From: Fan Wu @ 2026-07-03  5:01 UTC (permalink / raw)
  To: netdev
  Cc: shenjian15, salil.mehta, andrew+netdev, davem, edumazet, kuba,
	pabeni, linux-kernel, stable, Fan Wu

hip04_start_tx_timer() arms priv->tx_coalesce_timer from both the
TX path and the NAPI poll path. The timer callback, tx_done(), derives
priv from the timer and touches priv->napi, priv->reg_inten and
priv->base before scheduling NAPI.

The remove path currently frees the TX/RX rings before unregistering the
netdev. If the interface is still up, unregister_netdev() will run
.ndo_stop only after those rings have already been freed, while a pending
TX coalesce timer or NAPI instance can still reach the ring state. The
timer is also never cancelled before free_netdev() releases the netdev
private area.

Cancel the timer from hip04_mac_stop() after NAPI and the TX queue have
been disabled. In hip04_remove(), unregister the netdev first, drain the
timeout work and timer, and only then free the rings.

hip04_tx_timeout_task() can also restart the device by calling
hip04_mac_stop() followed by hip04_mac_open(). Serialize that restart with
rtnl_lock(), matching the netdev core's .ndo_stop locking, and skip it if
the device is no longer running.

Fixes: a41ea46a9a12 ("net: hisilicon: new hip04 ethernet driver")
Cc: stable@vger.kernel.org
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
index 18376bc..cb9b01c 100644
--- a/drivers/net/ethernet/hisilicon/hip04_eth.c
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -15,6 +15,7 @@
 #include <linux/of_net.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
+#include <linux/rtnetlink.h>
 
 #define SC_PPE_RESET_DREQ		0x026C
 
@@ -761,6 +762,13 @@ static int hip04_mac_stop(struct net_device *ndev)
 
 	napi_disable(&priv->napi);
 	netif_stop_queue(ndev);
+
+	/* Cancel the TX-coalesce timer after the arming paths (xmit via the
+	 * queue, rx poll via NAPI) are disabled, so a pending tx_done()
+	 * (which dereferences priv) is drained before the device is freed.
+	 */
+	hrtimer_cancel(&priv->tx_coalesce_timer);
+
 	hip04_mac_disable(ndev);
 	hip04_tx_reclaim(ndev, true);
 	hip04_reset_ppe(priv);
@@ -791,8 +799,15 @@ static void hip04_tx_timeout_task(struct work_struct *work)
 	struct hip04_priv *priv;
 
 	priv = container_of(work, struct hip04_priv, tx_timeout_task);
+
+	rtnl_lock();
+	if (!netif_running(priv->ndev))
+		goto out;
+
 	hip04_mac_stop(priv->ndev);
 	hip04_mac_open(priv->ndev);
+out:
+	rtnl_unlock();
 }
 
 static int hip04_get_coalesce(struct net_device *netdev,
@@ -1029,10 +1044,15 @@ static void hip04_remove(struct platform_device *pdev)
 	if (priv->phy)
 		phy_disconnect(priv->phy);
 
-	hip04_free_ring(ndev, d);
 	unregister_netdev(ndev);
-	of_node_put(priv->phy_node);
 	cancel_work_sync(&priv->tx_timeout_task);
+	hrtimer_cancel(&priv->tx_coalesce_timer);
+	/* Free the rings only after the interface is stopped (.ndo_stop via
+	 * unregister_netdev) and the work/timer are drained; the TX/NAPI
+	 * paths touch them while the device is up.
+	 */
+	hip04_free_ring(ndev, d);
+	of_node_put(priv->phy_node);
 	free_netdev(ndev);
 }
 


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

* Re: [PATCH] net: hip04: quiesce tx coalesce timer before teardown
  2026-07-03  5:01 [PATCH] net: hip04: quiesce tx coalesce timer before teardown Fan Wu
@ 2026-07-09 12:43 ` Simon Horman
  2026-07-11  5:34   ` Fan Wu
  0 siblings, 1 reply; 3+ messages in thread
From: Simon Horman @ 2026-07-09 12:43 UTC (permalink / raw)
  To: fanwu01
  Cc: Simon Horman, netdev, shenjian15, salil.mehta, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux-kernel, stable

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
net: hip04: quiesce tx coalesce timer before teardown

This patch fixes race conditions with the tx_coalesce_timer by properly
canceling it during device stop and remove. It also serializes device
restarts in the tx timeout handler using rtnl_lock().

> diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
> index 18376bcc718a2..cb9b01c79ee6e 100644
> --- a/drivers/net/ethernet/hisilicon/hip04_eth.c
> +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c

[ ... ]

> @@ -761,6 +762,13 @@ static int hip04_mac_stop(struct net_device *ndev)
>  
>  	napi_disable(&priv->napi);
>  	netif_stop_queue(ndev);

[Severity: High]
This is a pre-existing issue, but does netif_stop_queue() adequately protect
against concurrent transmission here?

Since netif_stop_queue() only changes the queue state flag without waiting for
an active ndo_start_xmit callback to finish (unlike netif_tx_disable()), could
this allow the subsequent hip04_tx_reclaim() call to corrupt the TX ring or
cause invalid DMA mappings if a transmission is actively running?

> +
> +	/* Cancel the TX-coalesce timer after the arming paths (xmit via the
> +	 * queue, rx poll via NAPI) are disabled, so a pending tx_done()
> +	 * (which dereferences priv) is drained before the device is freed.
> +	 */
> +	hrtimer_cancel(&priv->tx_coalesce_timer);

[Severity: High]
This is a pre-existing issue, but is it possible for the softirq to re-arm
the timer after hrtimer_cancel() is called?

In hip04_rx_poll(), the NAPI ownership is released via napi_complete_done()
before the timer is armed:

hip04_rx_poll() {
    ...
    napi_complete_done(napi, rx);
done:
    if (rx < budget && tx_remaining)
        hip04_start_tx_timer(priv);
}

If the teardown thread cancels the timer and frees the private structure
while the softirq is preempted just after napi_complete_done(), could this
result in a use-after-free when hip04_start_tx_timer() is eventually executed?

> +
>  	hip04_mac_disable(ndev);
>  	hip04_tx_reclaim(ndev, true);

[Severity: High]
This is a pre-existing issue, but is there missing hardware synchronization
between disabling the MAC and reclaiming the TX ring?

hip04_mac_disable() disables the MAC using a relaxed MMIO write without
polling for an idle state.

Since the very next call is hip04_tx_reclaim() which unmaps buffers and
modifies descriptor memory, could this cause silent data corruption or IOMMU
page faults if the MAC is still actively processing packets in the background?

[ ... ]

> @@ -791,8 +799,15 @@ static void hip04_tx_timeout_task(struct work_struct *work)
>  	struct hip04_priv *priv;
>  
>  	priv = container_of(work, struct hip04_priv, tx_timeout_task);
> +
> +	rtnl_lock();
> +	if (!netif_running(priv->ndev))
> +		goto out;
> +
>  	hip04_mac_stop(priv->ndev);
>  	hip04_mac_open(priv->ndev);

[Severity: High]
This is a pre-existing issue, but what happens if hip04_mac_open() fails
during this restart?

If dma_map_single() fails inside hip04_mac_open(), it returns -EIO without
enabling NAPI. Because this return value is ignored here, the device remains
broken and NAPI is left disabled (with the SCHED bit set).

Will this cause a hard lockup later when hip04_mac_stop() spins forever in
napi_disable() waiting for the SCHED bit to clear?

> +out:
> +	rtnl_unlock();
>  }
>  

[ ... ]

> @@ -1029,10 +1044,15 @@ static void hip04_remove(struct platform_device *pdev)
>  	if (priv->phy)
>  		phy_disconnect(priv->phy);
>  
> -	hip04_free_ring(ndev, d);
>  	unregister_netdev(ndev);

[Severity: High]
This is a pre-existing issue, but does calling phy_disconnect() before
unregister_netdev() cause a use-after-free and WARN?

phy_disconnect() halts and detaches the PHY, dropping its reference count.
The subsequent call to unregister_netdev() invokes hip04_mac_stop(), which
unconditionally calls phy_stop().

Could calling phy_stop() on a detached and potentially freed PHY device
pointer cause a crash?

> -	of_node_put(priv->phy_node);
>  	cancel_work_sync(&priv->tx_timeout_task);
> +	hrtimer_cancel(&priv->tx_coalesce_timer);
> +	/* Free the rings only after the interface is stopped (.ndo_stop via
> +	 * unregister_netdev) and the work/timer are drained; the TX/NAPI
> +	 * paths touch them while the device is up.
> +	 */
> +	hip04_free_ring(ndev, d);

[Severity: High]
This is a pre-existing issue, but does hip04_free_ring() safely handle
allocation failures?

During probe, if allocating the TX descriptor ring fails, priv->tx_desc
remains NULL, and the error path calls hip04_free_ring().

Since hip04_free_ring() unconditionally calls dma_free_coherent() on
priv->tx_desc, could this cause a kernel panic on architectures that trap
virtual-to-physical translations on NULL?

> +	of_node_put(priv->phy_node);
>  	free_netdev(ndev);

[Severity: High]
This is a pre-existing issue, but does explicitly freeing the netdev here
create a use-after-free race with the interrupt handler?

The driver requests its interrupt in hip04_mac_probe() using
devm_request_irq(), meaning the IRQ is only freed during devres cleanup after
hip04_remove() completely finishes.

If an interrupt fires between free_netdev() and the devres cleanup,
hip04_mac_interrupt() will cast dev_id to the freed ndev pointer, which seems
like it would lead to a use-after-free crash.

>  }
>  

[ ... ]

Regarding the RX polling path in hip04_rx_poll():

[Severity: High]
This is a pre-existing issue, but does a failure in build_skb() cause a memory
and DMA mapping leak?

Looking at hip04_rx_poll():

    if (unlikely(!skb)) {
        net_dbg_ratelimited("build_skb failed\n");
        goto refill;
    }
    ...
refill:
    ...
    priv->rx_buf[priv->rx_head] = buf;
    priv->rx_phys[priv->rx_head] = phys;

If build_skb() fails, the code jumps directly to the refill label, completely
bypassing the dma_unmap_single() call for the original buffer. It then
allocates and maps a new buffer, overwriting the array entries.

Could this permanently leak the original buffer and its DMA mapping,
eventually leading to an OOM crash under memory pressure?

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

* Re: [PATCH] net: hip04: quiesce tx coalesce timer before teardown
  2026-07-09 12:43 ` Simon Horman
@ 2026-07-11  5:34   ` Fan Wu
  0 siblings, 0 replies; 3+ messages in thread
From: Fan Wu @ 2026-07-11  5:34 UTC (permalink / raw)
  To: Simon Horman
  Cc: Fan Wu, netdev, shenjian15, salil.mehta, andrew+netdev,
	David S . Miller, edumazet, Jakub Kicinski, pabeni, linux-kernel,
	stable

Hi Simon,

Thanks for the v1 review. The updated patch fixes the PHY teardown ordering
and IRQ lifetime issues: hip04_remove() unregisters the netdev, running
.ndo_stop and phy_stop(), before phy_disconnect(), and frees the devm-managed
IRQ before free_netdev().

The remaining NULL tx_desc cleanup in hip04_free_ring() and RX refill
failure paths are independent pre-existing error-path issues. I will address them 
in separate patches rather than expanding this teardown fix. In particular, the
RX fix must preserve the old descriptor mapping until a replacement buffer is
successfully allocated and mapped.

The latest revision is v3:
https://lore.kernel.org/netdev/20260711052922.1634837-1-fanwu01@zju.edu.cn

Thanks,
Fan

> On Jul 9, 2026, at 20:43, Simon Horman <horms@kernel.org> wrote:
> 
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> net: hip04: quiesce tx coalesce timer before teardown
> 
> This patch fixes race conditions with the tx_coalesce_timer by properly
> canceling it during device stop and remove. It also serializes device
> restarts in the tx timeout handler using rtnl_lock().
> 
>> diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
>> index 18376bcc718a2..cb9b01c79ee6e 100644
>> --- a/drivers/net/ethernet/hisilicon/hip04_eth.c
>> +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
> 
> [ ... ]
> 
>> @@ -761,6 +762,13 @@ static int hip04_mac_stop(struct net_device *ndev)
>> 
>> napi_disable(&priv->napi);
>> netif_stop_queue(ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but does netif_stop_queue() adequately protect
> against concurrent transmission here?
> 
> Since netif_stop_queue() only changes the queue state flag without waiting for
> an active ndo_start_xmit callback to finish (unlike netif_tx_disable()), could
> this allow the subsequent hip04_tx_reclaim() call to corrupt the TX ring or
> cause invalid DMA mappings if a transmission is actively running?
> 
>> +
>> + /* Cancel the TX-coalesce timer after the arming paths (xmit via the
>> + * queue, rx poll via NAPI) are disabled, so a pending tx_done()
>> + * (which dereferences priv) is drained before the device is freed.
>> + */
>> + hrtimer_cancel(&priv->tx_coalesce_timer);
> 
> [Severity: High]
> This is a pre-existing issue, but is it possible for the softirq to re-arm
> the timer after hrtimer_cancel() is called?
> 
> In hip04_rx_poll(), the NAPI ownership is released via napi_complete_done()
> before the timer is armed:
> 
> hip04_rx_poll() {
>    ...
>    napi_complete_done(napi, rx);
> done:
>    if (rx < budget && tx_remaining)
>        hip04_start_tx_timer(priv);
> }
> 
> If the teardown thread cancels the timer and frees the private structure
> while the softirq is preempted just after napi_complete_done(), could this
> result in a use-after-free when hip04_start_tx_timer() is eventually executed?
> 
>> +
>> hip04_mac_disable(ndev);
>> hip04_tx_reclaim(ndev, true);
> 
> [Severity: High]
> This is a pre-existing issue, but is there missing hardware synchronization
> between disabling the MAC and reclaiming the TX ring?
> 
> hip04_mac_disable() disables the MAC using a relaxed MMIO write without
> polling for an idle state.
> 
> Since the very next call is hip04_tx_reclaim() which unmaps buffers and
> modifies descriptor memory, could this cause silent data corruption or IOMMU
> page faults if the MAC is still actively processing packets in the background?
> 
> [ ... ]
> 
>> @@ -791,8 +799,15 @@ static void hip04_tx_timeout_task(struct work_struct *work)
>> struct hip04_priv *priv;
>> 
>> priv = container_of(work, struct hip04_priv, tx_timeout_task);
>> +
>> + rtnl_lock();
>> + if (!netif_running(priv->ndev))
>> + goto out;
>> +
>> hip04_mac_stop(priv->ndev);
>> hip04_mac_open(priv->ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but what happens if hip04_mac_open() fails
> during this restart?
> 
> If dma_map_single() fails inside hip04_mac_open(), it returns -EIO without
> enabling NAPI. Because this return value is ignored here, the device remains
> broken and NAPI is left disabled (with the SCHED bit set).
> 
> Will this cause a hard lockup later when hip04_mac_stop() spins forever in
> napi_disable() waiting for the SCHED bit to clear?
> 
>> +out:
>> + rtnl_unlock();
>> }
>> 
> 
> [ ... ]
> 
>> @@ -1029,10 +1044,15 @@ static void hip04_remove(struct platform_device *pdev)
>> if (priv->phy)
>> phy_disconnect(priv->phy);
>> 
>> - hip04_free_ring(ndev, d);
>> unregister_netdev(ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but does calling phy_disconnect() before
> unregister_netdev() cause a use-after-free and WARN?
> 
> phy_disconnect() halts and detaches the PHY, dropping its reference count.
> The subsequent call to unregister_netdev() invokes hip04_mac_stop(), which
> unconditionally calls phy_stop().
> 
> Could calling phy_stop() on a detached and potentially freed PHY device
> pointer cause a crash?
> 
>> - of_node_put(priv->phy_node);
>> cancel_work_sync(&priv->tx_timeout_task);
>> + hrtimer_cancel(&priv->tx_coalesce_timer);
>> + /* Free the rings only after the interface is stopped (.ndo_stop via
>> + * unregister_netdev) and the work/timer are drained; the TX/NAPI
>> + * paths touch them while the device is up.
>> + */
>> + hip04_free_ring(ndev, d);
> 
> [Severity: High]
> This is a pre-existing issue, but does hip04_free_ring() safely handle
> allocation failures?
> 
> During probe, if allocating the TX descriptor ring fails, priv->tx_desc
> remains NULL, and the error path calls hip04_free_ring().
> 
> Since hip04_free_ring() unconditionally calls dma_free_coherent() on
> priv->tx_desc, could this cause a kernel panic on architectures that trap
> virtual-to-physical translations on NULL?
> 
>> + of_node_put(priv->phy_node);
>> free_netdev(ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but does explicitly freeing the netdev here
> create a use-after-free race with the interrupt handler?
> 
> The driver requests its interrupt in hip04_mac_probe() using
> devm_request_irq(), meaning the IRQ is only freed during devres cleanup after
> hip04_remove() completely finishes.
> 
> If an interrupt fires between free_netdev() and the devres cleanup,
> hip04_mac_interrupt() will cast dev_id to the freed ndev pointer, which seems
> like it would lead to a use-after-free crash.
> 
>> }
>> 
> 
> [ ... ]
> 
> Regarding the RX polling path in hip04_rx_poll():
> 
> [Severity: High]
> This is a pre-existing issue, but does a failure in build_skb() cause a memory
> and DMA mapping leak?
> 
> Looking at hip04_rx_poll():
> 
>    if (unlikely(!skb)) {
>        net_dbg_ratelimited("build_skb failed\n");
>        goto refill;
>    }
>    ...
> refill:
>    ...
>    priv->rx_buf[priv->rx_head] = buf;
>    priv->rx_phys[priv->rx_head] = phys;
> 
> If build_skb() fails, the code jumps directly to the refill label, completely
> bypassing the dma_unmap_single() call for the original buffer. It then
> allocates and maps a new buffer, overwriting the array entries.
> 
> Could this permanently leak the original buffer and its DMA mapping,
> eventually leading to an OOM crash under memory pressure?


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

end of thread, other threads:[~2026-07-11  5:34 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-03  5:01 [PATCH] net: hip04: quiesce tx coalesce timer before teardown Fan Wu
2026-07-09 12:43 ` Simon Horman
2026-07-11  5:34   ` Fan Wu

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