mmc: host: Shift pm qos unvoting to sofitrq

In eMMC driver CQ mode, the sdhc driver votes for pm qos and unvotes in
hardware irq when there are no more requests. But in a particular corner
scenario, the qos unvoting is not done by hardware irq handler after
last request and this can cause power regression. So the unvoting was
been shifted to software irq context of eMMC driver where there is
enough information to accurately know when there are no active requests to
the eMMC driver.

There is a very rare corner case when the active req counter is
checked by completion context and before it schedules the work to
unvote, the issuing context can set active req counter and do
qos voting. In this case the performance suffers but this is a
very rare corner case as the completion context is in softirq.
Also it is not good to add locking to prevent this corner case as
issuing and completion contexts should not have locking in between them.

Change-Id: I9fb714d3a5e4eb35a0bedd69691e3f1f84fc4cd8
Signed-off-by: Vijay Viswanath <vviswana@codeaurora.org>
This commit is contained in:
Vijay Viswanath 2017-05-17 11:04:10 +05:30 committed by Gerrit - the friendly Code Review server
parent fc3412db48
commit 4275ba78b3
5 changed files with 50 additions and 27 deletions

View File

@ -3118,7 +3118,7 @@ static void mmc_blk_cmdq_reset_all(struct mmc_host *host, int err)
if (!ret) {
WARN_ON(!test_and_clear_bit(itag,
&ctx_info->data_active_reqs));
mmc_cmdq_post_req(host, itag, err);
mmc_cmdq_post_req(host, itag, err, false);
} else {
clear_bit(CMDQ_STATE_DCMD_ACTIVE,
&ctx_info->curr_state);
@ -3338,8 +3338,8 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
else
BUG_ON(!test_and_clear_bit(cmdq_req->tag,
&ctx_info->data_active_reqs));
if (!is_dcmd)
mmc_cmdq_post_req(host, cmdq_req->tag, err);
mmc_cmdq_post_req(host, cmdq_req->tag, err, is_dcmd);
if (cmdq_req->cmdq_req_flags & DCMD) {
clear_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx_info->curr_state);
blk_end_request_all(rq, err);

View File

@ -974,10 +974,10 @@ EXPORT_SYMBOL(mmc_cmdq_discard_queue);
* @tag: the request tag.
* @err: non-zero is error, success otherwise
*/
void mmc_cmdq_post_req(struct mmc_host *host, int tag, int err)
void mmc_cmdq_post_req(struct mmc_host *host, int tag, int err, bool is_dcmd)
{
if (likely(host->cmdq_ops->post_req))
host->cmdq_ops->post_req(host, tag, err);
host->cmdq_ops->post_req(host, tag, err, is_dcmd);
}
EXPORT_SYMBOL(mmc_cmdq_post_req);

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -786,6 +786,14 @@ skip_cqterri:
mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
}
/*
* Incase of error we will schedule a work to do the unvoting
* of pm_qos here. This is because incase of error, softirq
* handler will not schedule unvoting of pm_qos. Incase of
* no error, the softirq will schedule work for pm_qos unvoting
* after the last request is complete (irrespective of
* whether the last request is DCMD or data request)
*/
if (cq_host->ops->pm_qos_update)
cq_host->ops->pm_qos_update(mmc, NULL, false);
@ -798,24 +806,6 @@ skip_cqterri:
if (!comp_status)
goto out;
/*
* pm-qos for cmdq is removed only when there is no cmdq
* request been processed.
* Check if comp_status matches with the number of active_reqs.
* This means that all reqs got actually completed and there
* was no DCMD.
* But in case of DCMD, active_reqs mask has a bit set for DCMD
* as well, so ensure that the when comp_status bit is set
* for DCMD then there should not be any data_active_reqs in
* flight (which can happen if DCMD is not set with QBR)
*/
if (((mmc->cmdq_ctx).active_reqs == comp_status) ||
(((1 << 31) & comp_status) &&
!((mmc->cmdq_ctx).data_active_reqs))) {
if (cq_host->ops->pm_qos_update)
cq_host->ops->pm_qos_update(mmc, NULL, false);
}
/*
* The CQTCN must be cleared before notifying req completion
* to upper layers to avoid missing completion notification
@ -921,7 +911,14 @@ static int cmdq_halt(struct mmc_host *mmc, bool halt)
return 0;
}
static void cmdq_post_req(struct mmc_host *mmc, int tag, int err)
/*
* Since this function from now on will be called for DCMD commands
* as well, it needs a new parameter as input to know if it is DCMD
* or not. This is because the tag we get for DCMD is almost always
* never the DCMD_SLOT tag.
*/
static void cmdq_post_req(struct mmc_host *mmc, int tag, int err, bool is_dcmd)
{
struct cmdq_host *cq_host;
struct mmc_request *mrq;
@ -931,6 +928,31 @@ static void cmdq_post_req(struct mmc_host *mmc, int tag, int err)
return;
cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
/*
* pm-qos for cmdq is removed only when there is no cmdq
* request been processed. Since this call occurs in softirq context
* ireespective of whether the request is a DCMD command or data
* request, we can use the active_reqs counter to decide whether
* any more requests are being actively processed by command queue
* thread.
*
* In case of error in cq request, the qos unvoting is done by the
* CQ hard irq handler. So no need to do unvoting here.
*
* There is a very rare corner case when the active req counter is
* checked by completion context and before it schedules the work to
* unvote, the issuing context can set active req counter and do
* qos voting. In this case the performance suffers but this is a
* very rare corner case as the completion context is in softirq.
*/
if (!err && !mmc->cmdq_ctx.active_reqs && cq_host->ops->pm_qos_update)
cq_host->ops->pm_qos_update(mmc, NULL, false);
if (is_dcmd)
return;
mrq = get_req_by_tag(cq_host, tag);
data = mrq->data;

View File

@ -118,7 +118,8 @@ struct mmc_cmdq_req;
extern int mmc_cmdq_discard_queue(struct mmc_host *host, u32 tasks);
extern int mmc_cmdq_halt(struct mmc_host *host, bool enable);
extern int mmc_cmdq_halt_on_empty_queue(struct mmc_host *host);
extern void mmc_cmdq_post_req(struct mmc_host *host, int tag, int err);
extern void mmc_cmdq_post_req(struct mmc_host *host, int tag, int err,
bool is_dcmd);
extern int mmc_cmdq_start_req(struct mmc_host *host,
struct mmc_cmdq_req *cmdq_req);
extern int mmc_cmdq_prepare_flush(struct mmc_command *cmd);

View File

@ -103,7 +103,7 @@ struct mmc_cmdq_host_ops {
int (*enable)(struct mmc_host *host);
void (*disable)(struct mmc_host *host, bool soft);
int (*request)(struct mmc_host *host, struct mmc_request *mrq);
void (*post_req)(struct mmc_host *host, int tag, int err);
void (*post_req)(struct mmc_host *host, int tag, int err, bool is_dcmd);
int (*halt)(struct mmc_host *host, bool halt);
void (*reset)(struct mmc_host *host, bool soft);
void (*dumpstate)(struct mmc_host *host);