diff options
author | Remi Pommarel <repk@triplefau.lt> | 2019-01-10 18:01:35 -0500 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2019-01-14 06:03:04 -0500 |
commit | bb364890323cca6e43f13e86d190ebf34a7d8cea (patch) | |
tree | 73b48a1dd6299be611cd61f3bf2ff8667a6dcc1b | |
parent | 287b1da6a458a30da2e5be745498d31092ebb001 (diff) |
mmc: meson-gx: Free irq in release() callback
Because the irq was requested through device managed resources API
(devm_request_threaded_irq()) it was freed after meson_mmc_remove()
completion, thus after mmc_free_host() has reclaimed meson_host memory.
As this irq is IRQF_SHARED, while using CONFIG_DEBUG_SHIRQ, its handler
get called by free_irq(). So meson_mmc_irq() was called after the
meson_host memory reclamation and was using invalid memory.
We ended up with the following scenario:
device_release_driver()
meson_mmc_remove()
mmc_free_host() /* Freeing host memory */
...
devres_release_all()
devm_irq_release()
__free_irq()
meson_mmc_irq() /* Uses freed memory */
To avoid this, the irq is released in meson_mmc_remove() and in
mseon_mmc_probe() error path before mmc_free_host() gets called.
Reported-by: Elie Roudninski <xademax@gmail.com>
Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Cc: stable@vger.kernel.org
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r-- | drivers/mmc/host/meson-gx-mmc.c | 18 |
1 files changed, 11 insertions, 7 deletions
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index c2690c1a50ff..f115d7c63ffe 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c | |||
@@ -179,6 +179,8 @@ struct meson_host { | |||
179 | struct sd_emmc_desc *descs; | 179 | struct sd_emmc_desc *descs; |
180 | dma_addr_t descs_dma_addr; | 180 | dma_addr_t descs_dma_addr; |
181 | 181 | ||
182 | int irq; | ||
183 | |||
182 | bool vqmmc_enabled; | 184 | bool vqmmc_enabled; |
183 | }; | 185 | }; |
184 | 186 | ||
@@ -1231,7 +1233,7 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
1231 | struct resource *res; | 1233 | struct resource *res; |
1232 | struct meson_host *host; | 1234 | struct meson_host *host; |
1233 | struct mmc_host *mmc; | 1235 | struct mmc_host *mmc; |
1234 | int ret, irq; | 1236 | int ret; |
1235 | 1237 | ||
1236 | mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev); | 1238 | mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev); |
1237 | if (!mmc) | 1239 | if (!mmc) |
@@ -1276,8 +1278,8 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
1276 | goto free_host; | 1278 | goto free_host; |
1277 | } | 1279 | } |
1278 | 1280 | ||
1279 | irq = platform_get_irq(pdev, 0); | 1281 | host->irq = platform_get_irq(pdev, 0); |
1280 | if (irq <= 0) { | 1282 | if (host->irq <= 0) { |
1281 | dev_err(&pdev->dev, "failed to get interrupt resource.\n"); | 1283 | dev_err(&pdev->dev, "failed to get interrupt resource.\n"); |
1282 | ret = -EINVAL; | 1284 | ret = -EINVAL; |
1283 | goto free_host; | 1285 | goto free_host; |
@@ -1331,9 +1333,8 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
1331 | writel(IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN, | 1333 | writel(IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN, |
1332 | host->regs + SD_EMMC_IRQ_EN); | 1334 | host->regs + SD_EMMC_IRQ_EN); |
1333 | 1335 | ||
1334 | ret = devm_request_threaded_irq(&pdev->dev, irq, meson_mmc_irq, | 1336 | ret = request_threaded_irq(host->irq, meson_mmc_irq, |
1335 | meson_mmc_irq_thread, IRQF_SHARED, | 1337 | meson_mmc_irq_thread, IRQF_SHARED, NULL, host); |
1336 | NULL, host); | ||
1337 | if (ret) | 1338 | if (ret) |
1338 | goto err_init_clk; | 1339 | goto err_init_clk; |
1339 | 1340 | ||
@@ -1351,7 +1352,7 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
1351 | if (host->bounce_buf == NULL) { | 1352 | if (host->bounce_buf == NULL) { |
1352 | dev_err(host->dev, "Unable to map allocate DMA bounce buffer.\n"); | 1353 | dev_err(host->dev, "Unable to map allocate DMA bounce buffer.\n"); |
1353 | ret = -ENOMEM; | 1354 | ret = -ENOMEM; |
1354 | goto err_init_clk; | 1355 | goto err_free_irq; |
1355 | } | 1356 | } |
1356 | 1357 | ||
1357 | host->descs = dma_alloc_coherent(host->dev, SD_EMMC_DESC_BUF_LEN, | 1358 | host->descs = dma_alloc_coherent(host->dev, SD_EMMC_DESC_BUF_LEN, |
@@ -1370,6 +1371,8 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
1370 | err_bounce_buf: | 1371 | err_bounce_buf: |
1371 | dma_free_coherent(host->dev, host->bounce_buf_size, | 1372 | dma_free_coherent(host->dev, host->bounce_buf_size, |
1372 | host->bounce_buf, host->bounce_dma_addr); | 1373 | host->bounce_buf, host->bounce_dma_addr); |
1374 | err_free_irq: | ||
1375 | free_irq(host->irq, host); | ||
1373 | err_init_clk: | 1376 | err_init_clk: |
1374 | clk_disable_unprepare(host->mmc_clk); | 1377 | clk_disable_unprepare(host->mmc_clk); |
1375 | err_core_clk: | 1378 | err_core_clk: |
@@ -1387,6 +1390,7 @@ static int meson_mmc_remove(struct platform_device *pdev) | |||
1387 | 1390 | ||
1388 | /* disable interrupts */ | 1391 | /* disable interrupts */ |
1389 | writel(0, host->regs + SD_EMMC_IRQ_EN); | 1392 | writel(0, host->regs + SD_EMMC_IRQ_EN); |
1393 | free_irq(host->irq, host); | ||
1390 | 1394 | ||
1391 | dma_free_coherent(host->dev, SD_EMMC_DESC_BUF_LEN, | 1395 | dma_free_coherent(host->dev, SD_EMMC_DESC_BUF_LEN, |
1392 | host->descs, host->descs_dma_addr); | 1396 | host->descs, host->descs_dma_addr); |