mmc: Do not perform blocking BKOPS
BKOPS operations can take long time and cause bad user experience and
failure of time critical operations as EFS sync.
In order to prevent the above failures all the levels will be handled only
in idle time BKOPS handling and will allow interrupting the BKOPS when
needed.
In case the card raised an exception with a need for BKOPS, a flag will
be set to indicate MMC to start the BKOPS activity when it becomes idle.
Fixed-CRs: 432027
Change-Id: I5f7b43569c0242f0fea83355f76f286b1ad037bc
Signed-off-by: Maya Erez <merez@codeaurora.org>
(cherry picked from commit 5f8ac3b955e44def61c2238192000ffe5f126714)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 9d996ac..b3cf4c7 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2029,9 +2029,12 @@
}
#endif
- if (req && !mq->mqrq_prev->req)
+ if (req && !mq->mqrq_prev->req) {
/* claim host only for the first request */
mmc_claim_host(card->host);
+ if (card->ext_csd.bkops_en)
+ mmc_stop_bkops(card);
+ }
ret = mmc_blk_part_switch(card, md);
if (ret) {
@@ -2067,9 +2070,12 @@
}
out:
- if (!req)
+ if (!req) {
+ if (mmc_card_need_bkops(card))
+ mmc_start_bkops(card, false);
/* release host only when there are no more requests */
mmc_release_host(card->host);
+ }
return ret;
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 8897f18a..baebe65 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -75,17 +75,6 @@
spin_unlock_irq(q->queue_lock);
if (req || mq->mqrq_prev->req) {
- /*
- * If this is the first request, BKOPs might be in
- * progress and needs to be stopped before issuing the
- * request
- */
- if (card->ext_csd.bkops_en &&
- card->bkops_info.started_delayed_bkops) {
- card->bkops_info.started_delayed_bkops = false;
- mmc_stop_bkops(card);
- }
-
set_current_state(TASK_RUNNING);
mq->issue_fn(mq, req);
} else {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2a2d807..95c4426 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -305,7 +305,6 @@
* it was removed from the queue work but not started yet
*/
card->bkops_info.cancel_delayed_work = false;
- card->bkops_info.started_delayed_bkops = true;
queue_delayed_work(system_nrt_wq, &card->bkops_info.dw,
msecs_to_jiffies(
card->bkops_info.delay_ms));
@@ -325,8 +324,6 @@
void mmc_start_bkops(struct mmc_card *card, bool from_exception)
{
int err;
- int timeout;
- bool use_busy_signal;
BUG_ON(!card);
if (!card->ext_csd.bkops_en)
@@ -347,58 +344,57 @@
goto out;
}
- err = mmc_read_bkops_status(card);
- if (err) {
- pr_err("%s: Failed to read bkops status: %d\n",
- mmc_hostname(card->host), err);
+ if (from_exception && mmc_card_need_bkops(card))
goto out;
- }
-
- if (!card->ext_csd.raw_bkops_status)
- goto out;
-
- pr_info("%s: %s: card->ext_csd.raw_bkops_status = 0x%x\n",
- mmc_hostname(card->host), __func__,
- card->ext_csd.raw_bkops_status);
/*
- * If the function was called due to exception but there is no need
- * for urgent BKOPS, BKOPs will be performed by the delayed BKOPs
- * work, before going to suspend
+ * If the need BKOPS flag is set, there is no need to check if BKOPS
+ * is needed since we already know that it does
*/
- if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
- from_exception)
- goto out;
+ if (!mmc_card_need_bkops(card)) {
+ err = mmc_read_bkops_status(card);
+ if (err) {
+ pr_err("%s: %s: Failed to read bkops status: %d\n",
+ mmc_hostname(card->host), __func__, err);
+ goto out;
+ }
- if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
- timeout = MMC_BKOPS_MAX_TIMEOUT;
- use_busy_signal = true;
- } else {
- timeout = 0;
- use_busy_signal = false;
+ if (!card->ext_csd.raw_bkops_status)
+ goto out;
+
+ pr_info("%s: %s: raw_bkops_status=0x%x, from_exception=%d\n",
+ mmc_hostname(card->host), __func__,
+ card->ext_csd.raw_bkops_status,
+ from_exception);
}
+ /*
+ * If the function was called due to exception, BKOPS will be performed
+ * after handling the last pending request
+ */
+ if (from_exception) {
+ pr_debug("%s: %s: Level %d from exception, exit",
+ mmc_hostname(card->host), __func__,
+ card->ext_csd.raw_bkops_status);
+ mmc_card_set_need_bkops(card);
+ goto out;
+ }
+ pr_info("%s: %s: Starting bkops\n", mmc_hostname(card->host), __func__);
+
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BKOPS_START, 1, timeout,
- use_busy_signal, use_busy_signal);
+ EXT_CSD_BKOPS_START, 1, 0, false, false);
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
goto out;
}
+ mmc_card_clr_need_bkops(card);
- /*
- * For urgent bkops status (LEVEL_2 and more)
- * bkops executed synchronously, otherwise
- * the operation is in progress
- */
- if (!use_busy_signal) {
- mmc_card_set_doing_bkops(card);
- pr_debug("%s: %s: starting the polling thread\n",
- mmc_hostname(card->host), __func__);
- queue_work(system_nrt_wq,
- &card->bkops_info.poll_for_completion);
- }
+ mmc_card_set_doing_bkops(card);
+ pr_debug("%s: %s: starting the polling thread\n",
+ mmc_hostname(card->host), __func__);
+ queue_work(system_nrt_wq,
+ &card->bkops_info.poll_for_completion);
out:
mmc_release_host(card->host);
@@ -452,7 +448,6 @@
pr_debug("%s: %s: completed BKOPs, exit polling\n",
mmc_hostname(card->host), __func__);
mmc_card_clr_doing_bkops(card);
- card->bkops_info.started_delayed_bkops = false;
goto out;
}
@@ -765,7 +760,9 @@
* Send HPI command to stop ongoing background operations to
* allow rapid servicing of foreground operations, e.g. read/
* writes. Wait until the card comes out of the programming state
- * to avoid errors in servicing read/write requests.
+ * to avoid errors in servicing read/write requests.
+ *
+ * The function should be called with host claimed.
*/
int mmc_stop_bkops(struct mmc_card *card)
{
@@ -773,8 +770,6 @@
BUG_ON(!card);
- mmc_claim_host(card->host);
-
/*
* Notify the delayed work to be cancelled, in case it was already
* removed from the queue, but was not started yet
@@ -797,7 +792,6 @@
}
out:
- mmc_release_host(card->host);
return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
@@ -2766,7 +2760,9 @@
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
if (host->card && mmc_card_mmc(host->card)) {
+ mmc_claim_host(host);
err = mmc_stop_bkops(host->card);
+ mmc_release_host(host);
if (err) {
pr_err("%s: didn't stop bkops\n",
mmc_hostname(host));
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 6a0f23a..8807ef1 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -239,8 +239,6 @@
* @poll_for_completion: Poll on BKOPS completion
* @cancel_delayed_work: A flag to indicate if the delayed work
* should be cancelled
- * @started_delayed_bkops: A flag to indicate if the delayed
- * work was scheduled
* @sectors_changed: number of sectors written or
* discard since the last idle BKOPS were scheduled
*/
@@ -259,7 +257,6 @@
#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS (4 * 60 * 1000) /* in ms */
#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
bool cancel_delayed_work;
- bool started_delayed_bkops;
unsigned int sectors_changed;
/*
* Since canceling the delayed work might have significant effect on the
@@ -295,6 +292,7 @@
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
+#define MMC_STATE_NEED_BKOPS (1<<11) /* card needs to do BKOPS */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@@ -466,6 +464,7 @@
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
+#define mmc_card_need_bkops(c) ((c)->state & MMC_STATE_NEED_BKOPS)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -479,7 +478,8 @@
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
-
+#define mmc_card_set_need_bkops(c) ((c)->state |= MMC_STATE_NEED_BKOPS)
+#define mmc_card_clr_need_bkops(c) ((c)->state &= ~MMC_STATE_NEED_BKOPS)
/*
* Quirk add/remove for MMC products.
*/