diff options
author | Ulf Hansson <ulf.hansson@linaro.org> | 2020-02-04 11:54:38 +0300 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2020-03-24 16:35:39 +0300 |
commit | d46a24a9d2dbe1a14988877e9e602d027cd35d43 (patch) | |
tree | c4decd980e4b7a42419de0b98970e88b91ede9cf /drivers/mmc | |
parent | f4498549e1690af7cba1eff8570da9c134cb80b5 (diff) | |
download | linux-d46a24a9d2dbe1a14988877e9e602d027cd35d43.tar.xz |
mmc: core: Throttle polling rate for CMD6
In mmc_poll_for_busy() we loop continuously, either by sending a CMD13 or
by invoking the ->card_busy() host ops, as to detect when the card stops
signaling busy. This behaviour is problematic as it may cause CPU hogging,
especially when the busy signal time reaches beyond a few ms.
Let's fix the issue by adding a throttling mechanism, that inserts a
usleep_range() in between the polling attempts. The sleep range starts at
32-64us, but increases for each loop by a factor of 2, up until the range
reaches ~32-64ms. In this way, we are able to keep the loop fine-grained
enough for short busy signaling times, while also not hogging the CPU for
longer times.
Note that, this change is inspired by the similar throttling mechanism that
we already use for mmc_do_erase().
Reported-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Baolin Wang <baolin.wang7@gmail.com>
Tested-by: Ludovic Barre <ludovic.barre@st.com>
Reviewed-by: Ludovic Barre <ludovic.barre@st.com>
Link: https://lore.kernel.org/r/20200204085449.32585-2-ulf.hansson@linaro.org
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index e025604e17d4..da424dcb57ad 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -456,6 +456,7 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, struct mmc_host *host = card->host; int err; unsigned long timeout; + unsigned int udelay = 32, udelay_max = 32768; u32 status = 0; bool expired = false; bool busy = false; @@ -500,6 +501,13 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, mmc_hostname(host), __func__); return -ETIMEDOUT; } + + /* Throttle the polling rate to avoid hogging the CPU. */ + if (busy) { + usleep_range(udelay, udelay * 2); + if (udelay < udelay_max) + udelay *= 2; + } } while (busy); return 0; |