Skip to content

Commit 6300d5c

Browse files
Nitin Rawatmartinkpetersen
authored andcommitted
scsi: ufs: ufs-qcom: Fix ESI null pointer dereference
ESI/MSI is a performance optimization feature that provides dedicated interrupts per MCQ hardware queue. This is optional feature and UFS MCQ should work with and without ESI feature. Commit e46a28c ("scsi: ufs: qcom: Remove the MSI descriptor abuse") brings a regression in ESI (Enhanced System Interrupt) configuration that causes a null pointer dereference when Platform MSI allocation fails. The issue occurs in when platform_device_msi_init_and_alloc_irqs() in ufs_qcom_config_esi() fails (returns -EINVAL) but the current code uses __free() macro for automatic cleanup free MSI resources that were never successfully allocated. Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008 Call trace: mutex_lock+0xc/0x54 (P) platform_device_msi_free_irqs_all+0x1c/0x40 ufs_qcom_config_esi+0x1d0/0x220 [ufs_qcom] ufshcd_config_mcq+0x28/0x104 ufshcd_init+0xa3c/0xf40 ufshcd_pltfrm_init+0x504/0x7d4 ufs_qcom_probe+0x20/0x58 [ufs_qcom] Fix by restructuring the ESI configuration to try MSI allocation first, before any other resource allocation and instead use explicit cleanup instead of __free() macro to avoid cleanup of unallocated resources. Tested on SM8750 platform with MCQ enabled, both with and without Platform ESI support. Fixes: e46a28c ("scsi: ufs: qcom: Remove the MSI descriptor abuse") Cc: Manivannan Sadhasivam <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: James Bottomley <[email protected]> Signed-off-by: Nitin Rawat <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 09d57d6 commit 6300d5c

File tree

1 file changed

+15
-24
lines changed

1 file changed

+15
-24
lines changed

drivers/ufs/host/ufs-qcom.c

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,17 +2070,6 @@ static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *data)
20702070
return IRQ_HANDLED;
20712071
}
20722072

2073-
static void ufs_qcom_irq_free(struct ufs_qcom_irq *uqi)
2074-
{
2075-
for (struct ufs_qcom_irq *q = uqi; q->irq; q++)
2076-
devm_free_irq(q->hba->dev, q->irq, q->hba);
2077-
2078-
platform_device_msi_free_irqs_all(uqi->hba->dev);
2079-
devm_kfree(uqi->hba->dev, uqi);
2080-
}
2081-
2082-
DEFINE_FREE(ufs_qcom_irq, struct ufs_qcom_irq *, if (_T) ufs_qcom_irq_free(_T))
2083-
20842073
static int ufs_qcom_config_esi(struct ufs_hba *hba)
20852074
{
20862075
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
@@ -2095,18 +2084,18 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
20952084
*/
20962085
nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
20972086

2098-
struct ufs_qcom_irq *qi __free(ufs_qcom_irq) =
2099-
devm_kcalloc(hba->dev, nr_irqs, sizeof(*qi), GFP_KERNEL);
2100-
if (!qi)
2101-
return -ENOMEM;
2102-
/* Preset so __free() has a pointer to hba in all error paths */
2103-
qi[0].hba = hba;
2104-
21052087
ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs,
21062088
ufs_qcom_write_msi_msg);
21072089
if (ret) {
2108-
dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret);
2109-
return ret;
2090+
dev_warn(hba->dev, "Platform MSI not supported or failed, continuing without ESI\n");
2091+
return ret; /* Continue without ESI */
2092+
}
2093+
2094+
struct ufs_qcom_irq *qi = devm_kcalloc(hba->dev, nr_irqs, sizeof(*qi), GFP_KERNEL);
2095+
2096+
if (!qi) {
2097+
platform_device_msi_free_irqs_all(hba->dev);
2098+
return -ENOMEM;
21102099
}
21112100

21122101
for (int idx = 0; idx < nr_irqs; idx++) {
@@ -2117,15 +2106,17 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
21172106
ret = devm_request_irq(hba->dev, qi[idx].irq, ufs_qcom_mcq_esi_handler,
21182107
IRQF_SHARED, "qcom-mcq-esi", qi + idx);
21192108
if (ret) {
2120-
dev_err(hba->dev, "%s: Fail to request IRQ for %d, err = %d\n",
2109+
dev_err(hba->dev, "%s: Failed to request IRQ for %d, err = %d\n",
21212110
__func__, qi[idx].irq, ret);
2122-
qi[idx].irq = 0;
2111+
/* Free previously allocated IRQs */
2112+
for (int j = 0; j < idx; j++)
2113+
devm_free_irq(hba->dev, qi[j].irq, qi + j);
2114+
platform_device_msi_free_irqs_all(hba->dev);
2115+
devm_kfree(hba->dev, qi);
21232116
return ret;
21242117
}
21252118
}
21262119

2127-
retain_and_null_ptr(qi);
2128-
21292120
if (host->hw_ver.major >= 6) {
21302121
ufshcd_rmwl(hba, ESI_VEC_MASK, FIELD_PREP(ESI_VEC_MASK, MAX_ESI_VEC - 1),
21312122
REG_UFS_CFG3);

0 commit comments

Comments
 (0)