Re: [PATCH] net: hip04: quiesce tx coalesce timer before teardown
From: Simon Horman <horms@kernel.org>
Date: 2026-07-09 12:48:30
Also in:
lkml, 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().
quoted hunk ↗ jump to hunk
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
[ ... ]
quoted hunk ↗ jump to hunk
@@ -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? [ ... ]
quoted hunk ↗ jump to hunk
@@ -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(); }
[ ... ]
quoted hunk ↗ jump to hunk
@@ -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?