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)
This commit is contained in:
Maya Erez 2013-01-16 09:43:45 +02:00 committed by Iliyan Malchev
parent ef16b3c15a
commit 888a37eb3b
4 changed files with 54 additions and 63 deletions

View file

@ -2028,9 +2028,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
}
#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) {
@ -2066,9 +2069,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
}
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;
}

View file

@ -74,17 +74,6 @@ static int mmc_queue_thread(void *d)
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 {

View file

@ -305,7 +305,6 @@ void mmc_start_delayed_bkops(struct mmc_card *card)
* 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 @@ EXPORT_SYMBOL(mmc_start_delayed_bkops);
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 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
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 @@ void mmc_bkops_completion_polling(struct work_struct *work)
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 @@ EXPORT_SYMBOL(mmc_wait_for_cmd);
* 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 @@ int mmc_stop_bkops(struct mmc_card *card)
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 @@ int mmc_stop_bkops(struct mmc_card *card)
}
out:
mmc_release_host(card->host);
return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
@ -2755,7 +2749,9 @@ int mmc_pm_notify(struct notifier_block *notify_block,
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));

View file

@ -239,8 +239,6 @@ struct mmc_part {
* @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 @@ struct mmc_bkops_info {
#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 @@ struct mmc_card {
#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 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#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 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#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.
*/