From 39c9af33129c729be813e302fbe4aecfef48064f Mon Sep 17 00:00:00 2001 From: Phil Elwell <phil@raspberrypi.com> Date: Fri, 28 Mar 2025 16:35:39 +0000 Subject: [PATCH 1/3] mmc: Speed up reboot with an absent card On devices with no card detect mechanism, the host ends up polling for the insertion of a card. This polling happens at multiple frequencies and with many steps (SDIO, SD, MMC), some of which involve timeouts. If a reboot is requested during one of those scans, it will stall, pointlessly, for up to a minute while it completes. Attempt to short circuit the rescan if the MMC interface is being removed. Signed-off-by: Phil Elwell <phil@raspberrypi.com> --- drivers/mmc/core/core.c | 15 ++++++++++++++- drivers/mmc/core/mmc_ops.c | 3 +++ drivers/mmc/core/sdio.c | 4 ++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 95d40fd263698c..bed4d7fe8b9cca 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -399,10 +399,16 @@ void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) struct mmc_command *cmd; while (1) { - wait_for_completion(&mrq->completion); + if (!host->rescan_disable) + wait_for_completion(&mrq->completion); cmd = mrq->cmd; + if (host->rescan_disable) { + cmd->error = -ENOENT; + break; + } + if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) break; @@ -619,6 +625,11 @@ EXPORT_SYMBOL(mmc_is_req_done); */ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) { + if (host->rescan_disable) { + mrq->cmd->error = -ENOENT; + return; + } + __mmc_start_req(host, mrq); if (!mrq->cap_cmd_during_tfr) @@ -2265,6 +2276,8 @@ void mmc_rescan(struct work_struct *work) for (i = 0; i < ARRAY_SIZE(freqs); i++) { unsigned int freq = freqs[i]; + if (host->rescan_disable) + return; if (freq > host->f_max) { if (i + 1 < ARRAY_SIZE(freqs)) continue; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 3b3adbddf6641e..7db55752a671d9 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -512,6 +512,9 @@ int __mmc_poll_for_busy(struct mmc_host *host, unsigned int period_us, timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1; do { + if (host->rescan_disable) + return -ENOENT; + /* * Due to the possibility of being preempted while polling, * check the expiration time first. diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 4b19b8a16b0968..0954d43e41365b 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -673,6 +673,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ocr |= R4_18V_PRESENT; try_again: + if (host->rescan_disable) { + err = -ENOENT; + goto remove; + } if (!retries) { pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host)); ocr &= ~R4_18V_PRESENT; From 0c7e4a4d03471d6a316a940fd79ecaa0dc482989 Mon Sep 17 00:00:00 2001 From: Phil Elwell <phil@raspberrypi.com> Date: Mon, 31 Mar 2025 11:24:35 +0100 Subject: [PATCH 2/3] mmc: sdhci: reduce default timeout to 5s Reduce the maximum time it takes to get to reboot by decreasing the default SDHCI timeout to 5 seconds. Signed-off-by: Phil Elwell <phil@raspberrypi.com> --- drivers/mmc/host/sdhci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9bb360efe5b857..80027f0ee8205e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1629,6 +1629,7 @@ static void sdhci_finish_data(struct sdhci_host *host) static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { + const unsigned long default_timeout_secs = 5; int flags; u32 mask; unsigned long timeout; @@ -1704,10 +1705,10 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) timeout = jiffies; if (host->data_timeout) timeout += nsecs_to_jiffies(host->data_timeout); - else if (!cmd->data && cmd->busy_timeout > 9000) + else if (!cmd->data && cmd->busy_timeout > (default_timeout_secs - 1) * 1000) timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; else - timeout += 10 * HZ; + timeout += default_timeout_secs * HZ; sdhci_mod_timer(host, cmd->mrq, timeout); if (host->use_external_dma) From a5804e5715411a4533d7ec06283847d6c226f29c Mon Sep 17 00:00:00 2001 From: Phil Elwell <phil@raspberrypi.com> Date: Mon, 31 Mar 2025 10:16:09 +0100 Subject: [PATCH 3/3] mmc: sdhci: Avoid a NULL pointer crash Two days into running a reboot test over the weekend, the SDHCI driver hit a NULL pointer in sdhci_needs_reset. It's not clear how that has been added by the changes for a faster reboot, but guard against it explicitly. Signed-off-by: Phil Elwell <phil@raspberrypi.com> --- drivers/mmc/host/sdhci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 80027f0ee8205e..64c44a74cb5966 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1490,6 +1490,8 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) { + if (!mrq) + return false; return (!(host->flags & SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->error) || (mrq->sbc && mrq->sbc->error) ||