The mhi_net driver does not coordinate with runtime PM, which allows the underlying MHI controller to be runtime-suspended while transmit, receive, or RX buffer refill operations are in progress. This can lead to stalled transfers or failed queueing once runtime PM is enabled in the MHI core. Add runtime PM reference counting to the mhi_net data path to keep the controller active for the duration of TX, RX, and buffer management operations. Enable runtime PM during probe and take/release references explicitly around these critical paths. Signed-off-by: Krishna Chaitanya Chundru --- drivers/net/mhi_net.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c index ae169929a9d8..5d7f9ccdb17b 100644 --- a/drivers/net/mhi_net.c +++ b/drivers/net/mhi_net.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -76,11 +77,19 @@ static netdev_tx_t mhi_ndo_xmit(struct sk_buff *skb, struct net_device *ndev) struct mhi_device *mdev = mhi_netdev->mdev; int err; + err = pm_runtime_get(&mdev->dev); + if (err < 0 && err != -EINPROGRESS) { + dev_err(&mdev->dev, "pm_runtime_get failed %d\n", err); + pm_runtime_put_noidle(&mdev->dev); + goto exit_drop; + } + err = mhi_queue_skb(mdev, DMA_TO_DEVICE, skb, skb->len, MHI_EOT); if (unlikely(err)) { net_err_ratelimited("%s: Failed to queue TX buf (%d)\n", ndev->name, err); dev_kfree_skb_any(skb); + pm_runtime_put(&mdev->dev); goto exit_drop; } @@ -251,6 +260,7 @@ static void mhi_net_ul_callback(struct mhi_device *mhi_dev, /* MHI layer stopping/resetting the UL channel */ if (mhi_res->transaction_status == -ENOTCONN) { u64_stats_update_end(&mhi_netdev->stats.tx_syncp); + pm_runtime_put(&mdev->dev); return; } @@ -261,6 +271,7 @@ static void mhi_net_ul_callback(struct mhi_device *mhi_dev, } u64_stats_update_end(&mhi_netdev->stats.tx_syncp); + pm_runtime_put(&mdev->dev); if (netif_queue_stopped(ndev) && !mhi_queue_is_full(mdev, DMA_TO_DEVICE)) netif_wake_queue(ndev); } @@ -277,6 +288,12 @@ static void mhi_net_rx_refill_work(struct work_struct *work) size = mhi_netdev->mru ? mhi_netdev->mru : READ_ONCE(ndev->mtu); + err = pm_runtime_resume_and_get(&mdev->dev); + if (err) { + dev_err(&mdev->dev, "pm_runtime_resume_and_get failed %d\n", err); + return; + } + while (!mhi_queue_is_full(mdev, DMA_FROM_DEVICE)) { skb = netdev_alloc_skb(ndev, size); if (unlikely(!skb)) @@ -296,6 +313,7 @@ static void mhi_net_rx_refill_work(struct work_struct *work) cond_resched(); } + pm_runtime_put(&mdev->dev); /* If we're still starved of rx buffers, reschedule later */ if (mhi_get_free_desc_count(mdev, DMA_FROM_DEVICE) == mhi_netdev->rx_queue_sz) schedule_delayed_work(&mhi_netdev->rx_refill, HZ / 2); @@ -362,20 +380,41 @@ static int mhi_net_probe(struct mhi_device *mhi_dev, SET_NETDEV_DEV(ndev, &mhi_dev->dev); + pm_runtime_no_callbacks(&mhi_dev->dev); + err = devm_pm_runtime_set_active_enabled(&mhi_dev->dev); + if (err) + return err; + + err = pm_runtime_resume_and_get(&mhi_dev->dev); + if (err) { + dev_err(&mhi_dev->dev, "pm_runtime_resume_and_get failed %d\n", err); + return err; + } + err = mhi_net_newlink(mhi_dev, ndev); if (err) { free_netdev(ndev); + pm_runtime_put(&mhi_dev->dev); return err; } + pm_runtime_put(&mhi_dev->dev); return 0; } static void mhi_net_remove(struct mhi_device *mhi_dev) { struct mhi_net_dev *mhi_netdev = dev_get_drvdata(&mhi_dev->dev); + int err; + + err = pm_runtime_resume_and_get(&mhi_dev->dev); + if (err) + dev_err(&mhi_dev->dev, "pm_runtime_resume_and_get failed %d\n", err); mhi_net_dellink(mhi_dev, mhi_netdev->ndev); + + if (!err) + pm_runtime_put(&mhi_dev->dev); } static const struct mhi_device_info mhi_hwip0 = { -- 2.34.1