MMC core:

- Prevent potential use-after-free for SDIO IRQ work
  - Fix OF node reference leak on card add failure
  - Fix memory lea when the port table is full for sdio_uart
 
 MMC host:
  - hsq: Fix use-after-free in retry work
  - mmci: Fix use-after-free in busy-timeout work
  - mmc_spi: Reset bytes_xfered before retrying CRC failures
  - mxcmmc: Cancel data work and watchdog on remove
  - rtsx_pci_sdmmc: Ignore broken write-protect on ThinkPad X260
  - sdhci_am654: A couple of fixes for the tuning sequence
  - sdhci-of-aspeed: Remove children before releasing SDC resources
  - sh_mmcif: Initialize IRQ-thread mutex before requesting interrupt
 
 MEMSTICK:
  - ms_block: Destroy io_queue workqueue on removal
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmqtI4oQHHVsZmhAa2Vy
 bmVsLm9yZwAKCRD+JoQlc1iMKcjwD/91lyNP8fb8cxorEtmkiNt63Sb+glVEnobW
 aOlXS23GuV9ZsPw7kKEEfZ4EWCOTEZ86Lj0OMzlpXE8dxYHGlu0qG97uxSiJ4wux
 LQ0+OR5ljqDN48BWrn3wWsJ1YLfM4xXL7XukiCEuxLU9jwlbPHNjtY8c0+JqMapH
 D1yE7tH7/ZPJuYwKt9DJlx5DKg0BTiRn/7j+o3IETNyuBP05ZzrUjNdgQW8DVawg
 2uR0GCZ268Asd7XhnywvLXbeg5jxRAEVMGVjEzn2CY5uY0YyUW9ye9e+TpbNqpYf
 OA2MnYiTNpcRIiI3Z8R5vrhGxMpqFd5hFdIUE64O5gnjSyrBKeo9SSw4huXlLvac
 xjdBYmtLxSQjIgvZaEG4hl12lJt/snLJODTEq690zei9oWCUnCKzZaargLFPcDje
 0J8HwPYStynI0nc2Jc4oGZEGGwgvSmFPk15JtMEdmJb3eIwOfzGLA1ky9+5VEVCC
 zzMifnvnseAJwz+iAaJYxkED3Gd4t/jm4n8XIihddsKBQHAbMtGhUmhSzNnzD6NF
 xgscpv8s8SKExwS6X+6f/SBZD4VoF9b1JDhJ+fVUJDQ1Dsgv9AyT/bl8gDBHPNwr
 JHflPaQhc3hM9RppdcZnW3KSCu3DCJ04XaoHy6m5zKWZHUBEAtxujHA5u373Wv89
 StY7v7d7UQ==
 =KPXN
 -----END PGP SIGNATURE-----

Merge tag 'mmc-v7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC/MEMSTICK fixes from Ulf Hansson:
 "MMC core:
   - Prevent potential use-after-free for SDIO IRQ work
   - Fix OF node reference leak on card add failure
   - Fix memory lea when the port table is full for sdio_uart

  MMC host:
   - hsq: Fix use-after-free in retry work
   - mmci: Fix use-after-free in busy-timeout work
   - mmc_spi: Reset bytes_xfered before retrying CRC failures
   - mxcmmc: Cancel data work and watchdog on remove
   - rtsx_pci_sdmmc: Ignore broken write-protect on ThinkPad X260
   - sdhci_am654: A couple of fixes for the tuning sequence
   - sdhci-of-aspeed: Remove children before releasing SDC resources
   - sh_mmcif: Initialize IRQ-thread mutex before requesting interrupt

  MEMSTICK:
   - ms_block: Destroy io_queue workqueue on removal

* tag 'mmc-v7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc:
  mmc: sdhci-of-aspeed: Remove children before releasing SDC resources
  mmc: sh_mmcif: initialize IRQ-thread mutex before requesting interrupt
  mmc: core: Fix OF node reference leak on card add failure
  mmc: rtsx_pci_sdmmc: ignore broken write-protect on ThinkPad X260
  mmc: sdio_uart: fix xmit_fifo leak when the port table is full
  mmc: spi: reset bytes_xfered before retrying CRC failures
  mmc: sdhci_am654: Fallback to DT-provided itap delay on DDR50 tuning failure
  mmc: sdhci_am654: Clear ITAPDLY on tuning failure
  mmc: sdhci_am654: Reset command and data lines on failed tuning
  mmc: sdhci_am654: Move tuning_loop to local variable
  mmc: hsq: Fix use-after-free in retry work
  mmc: mxcmmc: cancel data work and watchdog on remove
  mmc: mmci: Fix use-after-free in busy-timeout work
  mmc: core: Cancel SDIO IRQ work before freeing host
  memstick: ms_block: destroy io_queue workqueue on removal
This commit is contained in:
Linus Torvalds 2026-09-18 10:53:42 -07:00
commit 8cb0606271
12 changed files with 60 additions and 20 deletions

View File

@ -2204,6 +2204,8 @@ static void msb_remove(struct memstick_dev *card)
msb_data_clear(msb);
mutex_unlock(&msb_disk_lock);
destroy_workqueue(msb->io_queue);
put_disk(msb->disk);
memstick_set_drvdata(card, NULL);
}

View File

@ -417,8 +417,8 @@ void mmc_remove_card(struct mmc_card *card)
mmc_hostname(card->host), card->rca);
}
device_del(&card->dev);
of_node_put(card->dev.of_node);
}
of_node_put(card->dev.of_node);
if (host->cqe_enabled) {
host->cqe_ops->cqe_disable(host);

View File

@ -698,6 +698,7 @@ EXPORT_SYMBOL(mmc_remove_host);
void mmc_free_host(struct mmc_host *host)
{
cancel_delayed_work_sync(&host->detect);
cancel_work_sync(&host->sdio_irq_work);
mmc_pwrseq_free(host);
put_device(&host->class_dev);
}

View File

@ -104,6 +104,9 @@ static int sdio_uart_add_port(struct sdio_uart_port *port)
}
spin_unlock(&sdio_uart_table_lock);
if (ret)
kfifo_free(&port->xmit_fifo);
return ret;
}

View File

@ -7,6 +7,7 @@
* Author: Baolin Wang <baolin.wang@linaro.org>
*/
#include <linux/devm-helpers.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
@ -345,6 +346,7 @@ static const struct mmc_cqe_ops mmc_hsq_ops = {
int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
{
int ret;
int i;
hsq->num_slots = HSQ_NUM_SLOTS;
hsq->next_tag = HSQ_INVALID_TAG;
@ -363,7 +365,11 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
for (i = 0; i < HSQ_NUM_SLOTS; i++)
hsq->tag_slot[i] = HSQ_INVALID_TAG;
INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
ret = devm_work_autocancel(mmc_dev(mmc), &hsq->retry_work,
mmc_hsq_retry_handler);
if (ret)
return ret;
spin_lock_init(&hsq->lock);
init_waitqueue_head(&hsq->wait_queue);

View File

@ -952,6 +952,7 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
status = mmc_spi_command_send(host, mrq, &stop, 0);
crc_retry--;
mrq->data->error = 0;
mrq->data->bytes_xfered = 0;
goto crc_recover;
}

View File

@ -2511,6 +2511,9 @@ static void mmci_remove(struct amba_device *dev)
writel(0, host->base + MMCICOMMAND);
writel(0, host->base + MMCIDATACTRL);
if (variant->busy_detect)
disable_delayed_work_sync(&host->ux500_busy_timeout_work);
mmci_dma_release(host);
clk_disable_unprepare(host->clk);
}

View File

@ -1173,6 +1173,10 @@ static void mxcmci_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
devm_free_irq(&pdev->dev, platform_get_irq(pdev, 0), host);
cancel_work_sync(&host->datawork);
timer_delete_sync(&host->watchdog);
if (host->pdata && host->pdata->exit)
host->pdata->exit(&pdev->dev, mmc);

View File

@ -1425,6 +1425,11 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM;
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
MMC_CAP2_NO_SDIO;
if (pcr->pci->device == 0x522a &&
pcr->pci->subsystem_vendor == PCI_VENDOR_ID_LENOVO &&
pcr->pci->subsystem_device == 0x504a)
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
mmc->max_current_330 = 400;
mmc->max_current_180 = 800;
mmc->ops = &realtek_pci_sdmmc_ops;

View File

@ -560,12 +560,14 @@ static int aspeed_sdc_probe(struct platform_device *pdev)
cpdev = of_platform_device_create(child, NULL, &pdev->dev);
if (!cpdev) {
ret = -ENODEV;
goto err_clk;
goto err_children;
}
}
return 0;
err_children:
device_for_each_child_reverse(&pdev->dev, NULL, of_platform_device_destroy);
err_clk:
clk_disable_unprepare(sdc->clk);
return ret;
@ -575,6 +577,7 @@ static void aspeed_sdc_remove(struct platform_device *pdev)
{
struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev);
device_for_each_child_reverse(&pdev->dev, NULL, of_platform_device_destroy);
clk_disable_unprepare(sdc->clk);
}

View File

@ -126,7 +126,7 @@ static const struct timing_data td[] = {
NULL,
MMC_CAP_UHS_SDR104},
[MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50",
NULL,
"ti,itap-del-sel-ddr50",
MMC_CAP_UHS_DDR50},
[MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52",
"ti,itap-del-sel-ddr52",
@ -144,6 +144,8 @@ struct sdhci_am654_data {
u32 otap_del_sel[ARRAY_SIZE(td)];
u32 itap_del_sel[ARRAY_SIZE(td)];
u32 itap_del_ena[ARRAY_SIZE(td)];
u32 itap_del_sel_dt_ddr50;
u32 itap_del_ena_dt_ddr50;
int clkbuf_sel;
int trm_icp;
int drv_strength;
@ -151,7 +153,6 @@ struct sdhci_am654_data {
u32 flags;
u32 quirks;
bool dll_enable;
u32 tuning_loop;
#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
#define SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA BIT(1)
@ -443,15 +444,13 @@ static int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode)
struct sdhci_host *host = mmc_priv(mmc);
int err = sdhci_execute_tuning(mmc, opcode);
if (err)
return err;
/*
* Tuning data remains in the buffer after tuning.
* Do a command and data reset to get rid of it
*/
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
return 0;
return err;
}
static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask)
@ -530,7 +529,6 @@ static int sdhci_am654_do_tuning(struct sdhci_host *host,
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
unsigned char timing = host->mmc->ios.timing;
struct window fail_window[ITAPDLY_LENGTH];
struct device *dev = mmc_dev(host->mmc);
u8 curr_pass, itap;
@ -539,11 +537,8 @@ static int sdhci_am654_do_tuning(struct sdhci_host *host,
memset(fail_window, 0, sizeof(fail_window));
/* Enable ITAPDLY */
sdhci_am654->itap_del_ena[timing] = 0x1;
for (itap = 0; itap < ITAPDLY_LENGTH; itap++) {
sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]);
sdhci_am654_write_itapdly(sdhci_am654, itap, 0x1);
curr_pass = !mmc_send_tuning(host->mmc, opcode, NULL);
@ -576,20 +571,36 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
unsigned char timing = host->mmc->ios.timing;
struct device *dev = mmc_dev(host->mmc);
unsigned int tuning_loop = 0;
int itapdly;
do {
itapdly = sdhci_am654_do_tuning(host, opcode);
if (itapdly >= 0)
break;
} while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX);
} while (++tuning_loop < RETRY_TUNING_MAX);
if (itapdly < 0) {
dev_err(dev, "Failed to find itapdly, fail tuning\n");
if (timing == MMC_TIMING_UHS_DDR50) {
dev_dbg(dev, "Failed DDR50 tuning, fallback to DT ITAP\n");
sdhci_am654->itap_del_sel[timing] = sdhci_am654->itap_del_sel_dt_ddr50;
sdhci_am654->itap_del_ena[timing] = sdhci_am654->itap_del_ena_dt_ddr50;
} else {
dev_err(dev, "Failed to find itapdly, fail tuning\n");
sdhci_am654->itap_del_ena[timing] = 0;
sdhci_am654->itap_del_sel[timing] = 0;
}
sdhci_am654_write_itapdly(sdhci_am654,
sdhci_am654->itap_del_sel[timing],
sdhci_am654->itap_del_ena[timing]);
return -1;
}
dev_dbg(dev, "Passed tuning, final itapdly=%d\n", itapdly);
/* Enable ITAPDLY */
sdhci_am654->itap_del_ena[timing] = 0x1;
sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
/* Save ITAPDLY */
sdhci_am654->itap_del_sel[timing] = itapdly;
@ -758,6 +769,11 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
}
}
sdhci_am654->itap_del_sel_dt_ddr50 =
sdhci_am654->itap_del_sel[MMC_TIMING_UHS_DDR50];
sdhci_am654->itap_del_ena_dt_ddr50 =
sdhci_am654->itap_del_ena[MMC_TIMING_UHS_DDR50];
return 0;
}
@ -806,9 +822,6 @@ static int sdhci_am654_init(struct sdhci_host *host)
regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK,
TUNINGFORSDR50_MASK);
/* Use to re-execute tuning */
sdhci_am654->tuning_loop = 0;
ret = sdhci_setup_host(host);
if (ret)
return ret;

View File

@ -1461,6 +1461,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
host->pd = pdev;
spin_lock_init(&host->lock);
mutex_init(&host->thread_lock);
mmc->ops = &sh_mmcif_ops;
sh_mmcif_init_ocr(host);
@ -1515,8 +1516,6 @@ static int sh_mmcif_probe(struct platform_device *pdev)
goto err_clk;
}
mutex_init(&host->thread_lock);
ret = mmc_add_host(mmc);
if (ret < 0)
goto err_clk;