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