]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: qrtr: Drop the MHI auto_queue feature for IPCR DL channels
authorManivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Thu, 18 Dec 2025 16:51:44 +0000 (22:21 +0530)
committerManivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Wed, 31 Dec 2025 10:54:04 +0000 (16:24 +0530)
MHI stack offers the 'auto_queue' feature, which allows the MHI stack to
auto queue the buffers for the RX path (DL channel). Though this feature
simplifies the client driver design, it introduces race between the client
drivers and the MHI stack. For instance, with auto_queue, the 'dl_callback'
for the DL channel may get called before the client driver is fully probed.
This means, by the time the dl_callback gets called, the client driver's
structures might not be initialized, leading to NULL ptr dereference.

Currently, the drivers have to workaround this issue by initializing the
internal structures before calling mhi_prepare_for_transfer_autoqueue().
But even so, there is a chance that the client driver's internal code path
may call the MHI queue APIs before mhi_prepare_for_transfer_autoqueue() is
called, leading to similar NULL ptr dereference. This issue has been
reported on the Qcom X1E80100 CRD machines affecting boot.

So to properly fix all these races, drop the MHI 'auto_queue' feature
altogether and let the client driver (QRTR) manage the RX buffers manually.
In the QRTR driver, queue the RX buffers based on the ring length during
probe and recycle the buffers in 'dl_callback' once they are consumed. This
also warrants removing the setting of 'auto_queue' flag from controller
drivers.

Currently, this 'auto_queue' feature is only enabled for IPCR DL channel.
So only the QRTR client driver requires the modification.

Fixes: 227fee5fc99e ("bus: mhi: core: Add an API for auto queueing buffers for DL channel")
Fixes: 68a838b84eff ("net: qrtr: start MHI channel after endpoit creation")
Reported-by: Johan Hovold <johan@kernel.org>
Closes: https://lore.kernel.org/linux-arm-msm/ZyTtVdkCCES0lkl4@hovoldconsulting.com
Suggested-by: Chris Lew <quic_clew@quicinc.com>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Acked-by: Jeff Johnson <jjohnson@kernel.org> # drivers/net/wireless/ath/...
Acked-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20251218-qrtr-fix-v2-1-c7499bfcfbe0@oss.qualcomm.com
drivers/accel/qaic/mhi_controller.c
drivers/bus/mhi/host/pci_generic.c
drivers/net/wireless/ath/ath11k/mhi.c
drivers/net/wireless/ath/ath12k/mhi.c
net/qrtr/mhi.c

index 13a14c6c61689fa4af47dade6d62b3cb1a148354..4d787f77ce419fcd2b250f9cabaec9c26f2da8dc 100644 (file)
@@ -39,7 +39,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -55,7 +54,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -71,7 +69,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -87,7 +84,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -103,7 +99,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -119,7 +114,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -135,7 +129,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -151,7 +144,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -167,7 +159,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -183,7 +174,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -199,7 +189,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -215,7 +204,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -231,7 +219,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -247,7 +234,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -263,7 +249,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -279,7 +264,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -295,7 +279,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -311,7 +294,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -327,7 +309,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -343,7 +324,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -359,7 +339,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -375,7 +354,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -391,7 +369,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -407,7 +384,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -423,7 +399,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -439,7 +414,6 @@ static const struct mhi_channel_config aic100_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = true,
                .wake_capable = false,
        },
 };
@@ -458,7 +432,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -474,7 +447,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -490,7 +462,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -506,7 +477,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -522,7 +492,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -538,7 +507,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -554,7 +522,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -570,7 +537,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -586,7 +552,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -602,7 +567,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -618,7 +582,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -634,7 +597,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -650,7 +612,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -666,7 +627,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -682,7 +642,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -698,7 +657,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -714,7 +672,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
                .wake_capable = false,
        },
        {
@@ -730,7 +687,6 @@ static const struct mhi_channel_config aic200_channels[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = true,
                .wake_capable = false,
        },
 };
index e3bc737313a2f0658bc9b9c4f7d85258aec2474c..0884a384b77fc3f56fa62a12351933132ffc9293 100644 (file)
@@ -94,22 +94,6 @@ struct mhi_pci_dev_info {
                .doorbell_mode_switch = false,          \
        }
 
-#define MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(ch_num, ch_name, el_count, ev_ring) \
-       {                                               \
-               .num = ch_num,                          \
-               .name = ch_name,                        \
-               .num_elements = el_count,               \
-               .event_ring = ev_ring,                  \
-               .dir = DMA_FROM_DEVICE,                 \
-               .ee_mask = BIT(MHI_EE_AMSS),            \
-               .pollcfg = 0,                           \
-               .doorbell = MHI_DB_BRST_DISABLE,        \
-               .lpm_notify = false,                    \
-               .offload_channel = false,               \
-               .doorbell_mode_switch = false,          \
-               .auto_queue = true,                     \
-       }
-
 #define MHI_EVENT_CONFIG_CTRL(ev_ring, el_count) \
        {                                       \
                .num_elements = el_count,       \
@@ -329,7 +313,7 @@ static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
        MHI_CHANNEL_CONFIG_UL(14, "QMI", 4, 0),
        MHI_CHANNEL_CONFIG_DL(15, "QMI", 4, 0),
        MHI_CHANNEL_CONFIG_UL(20, "IPCR", 8, 0),
-       MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 8, 0),
+       MHI_CHANNEL_CONFIG_DL(21, "IPCR", 8, 0),
        MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
        MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
        MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 64, 2),
@@ -762,7 +746,7 @@ static const struct mhi_channel_config mhi_telit_fn980_hw_v1_channels[] = {
        MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0),
        MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0),
        MHI_CHANNEL_CONFIG_UL(20, "IPCR", 16, 0),
-       MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 16, 0),
+       MHI_CHANNEL_CONFIG_DL(21, "IPCR", 16, 0),
        MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 1),
        MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 2),
 };
index acd76e9392d31192aca6776319ef0829a1c69628..d2c44f7f9b622601ea547870c0c7f37507c2eba5 100644 (file)
@@ -34,7 +34,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
        },
        {
                .num = 21,
@@ -48,7 +47,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = true,
        },
 };
 
@@ -99,7 +97,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
        },
        {
                .num = 21,
@@ -113,7 +110,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = true,
        },
 };
 
index 08f44baf182a5e34651e8c117fe279942f8ad8f4..2dbdb95ae7bea62557bb620e4759107e673b3bbd 100644 (file)
@@ -31,7 +31,6 @@ static const struct mhi_channel_config ath12k_mhi_channels_qcn9274[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
        },
        {
                .num = 21,
@@ -45,7 +44,6 @@ static const struct mhi_channel_config ath12k_mhi_channels_qcn9274[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = true,
        },
 };
 
@@ -96,7 +94,6 @@ static const struct mhi_channel_config ath12k_mhi_channels_wcn7850[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = false,
        },
        {
                .num = 21,
@@ -110,7 +107,6 @@ static const struct mhi_channel_config ath12k_mhi_channels_wcn7850[] = {
                .lpm_notify = false,
                .offload_channel = false,
                .doorbell_mode_switch = false,
-               .auto_queue = true,
        },
 };
 
index 69f53625a049ded5ca9a1d46d7c990c53881b88c..80e341d2f8a4521cd7ccc515bc3012d0bc5008be 100644 (file)
@@ -24,13 +24,25 @@ static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
        struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
        int rc;
 
-       if (!qdev || mhi_res->transaction_status)
+       if (!qdev || (mhi_res->transaction_status && mhi_res->transaction_status != -ENOTCONN))
                return;
 
+       /* Channel got reset. So just free the buffer */
+       if (mhi_res->transaction_status == -ENOTCONN) {
+               devm_kfree(&mhi_dev->dev, mhi_res->buf_addr);
+               return;
+       }
+
        rc = qrtr_endpoint_post(&qdev->ep, mhi_res->buf_addr,
                                mhi_res->bytes_xferd);
        if (rc == -EINVAL)
                dev_err(qdev->dev, "invalid ipcrouter packet\n");
+
+       /* Done with the buffer, now recycle it for future use */
+       rc = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, mhi_res->buf_addr,
+                          mhi_dev->mhi_cntrl->buffer_len, MHI_EOT);
+       if (rc)
+               dev_err(&mhi_dev->dev, "Failed to recycle the buffer: %d\n", rc);
 }
 
 /* From QRTR to MHI */
@@ -72,6 +84,29 @@ free_skb:
        return rc;
 }
 
+static int qcom_mhi_qrtr_queue_dl_buffers(struct mhi_device *mhi_dev)
+{
+       u32 free_desc;
+       void *buf;
+       int ret;
+
+       free_desc = mhi_get_free_desc_count(mhi_dev, DMA_FROM_DEVICE);
+       while (free_desc--) {
+               buf = devm_kmalloc(&mhi_dev->dev, mhi_dev->mhi_cntrl->buffer_len, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+
+               ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, buf, mhi_dev->mhi_cntrl->buffer_len,
+                                   MHI_EOT);
+               if (ret) {
+                       dev_err(&mhi_dev->dev, "Failed to queue buffer: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev,
                               const struct mhi_device_id *id)
 {
@@ -87,20 +122,30 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev,
        qdev->ep.xmit = qcom_mhi_qrtr_send;
 
        dev_set_drvdata(&mhi_dev->dev, qdev);
-       rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO);
-       if (rc)
-               return rc;
 
        /* start channels */
-       rc = mhi_prepare_for_transfer_autoqueue(mhi_dev);
-       if (rc) {
-               qrtr_endpoint_unregister(&qdev->ep);
+       rc = mhi_prepare_for_transfer(mhi_dev);
+       if (rc)
                return rc;
-       }
+
+       rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO);
+       if (rc)
+               goto err_unprepare;
+
+       rc = qcom_mhi_qrtr_queue_dl_buffers(mhi_dev);
+       if (rc)
+               goto err_unregister;
 
        dev_dbg(qdev->dev, "Qualcomm MHI QRTR driver probed\n");
 
        return 0;
+
+err_unregister:
+       qrtr_endpoint_unregister(&qdev->ep);
+err_unprepare:
+       mhi_unprepare_from_transfer(mhi_dev);
+
+       return rc;
 }
 
 static void qcom_mhi_qrtr_remove(struct mhi_device *mhi_dev)
@@ -151,11 +196,13 @@ static int __maybe_unused qcom_mhi_qrtr_pm_resume_early(struct device *dev)
        if (state == MHI_STATE_M3)
                return 0;
 
-       rc = mhi_prepare_for_transfer_autoqueue(mhi_dev);
-       if (rc)
+       rc = mhi_prepare_for_transfer(mhi_dev);
+       if (rc) {
                dev_err(dev, "failed to prepare for autoqueue transfer %d\n", rc);
+               return rc;
+       }
 
-       return rc;
+       return qcom_mhi_qrtr_queue_dl_buffers(mhi_dev);
 }
 
 static const struct dev_pm_ops qcom_mhi_qrtr_pm_ops = {