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 --- 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 #include #include +#include #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); }