mmc: core: Add support for idle time BKOPs
Subsystems (2)
PATCH 1/1 mmc: core: Add support for idle time BKOPs
When the mmcqd thread is idle, a delayed work is created to check the need for BKOPs. The time to start the delayed work is calculated based on the host controller suspend timeout, in case it was set. If not, a default time is used.
If BKOPs is required in level 1, which is non-blocking, there will be polling of the card status to wait for the BKOPs completion and prevent suspend that will interrupt the BKOPs.
If the card raised an exception, the need for urgent BKOPs (level 2/3) will be checked immediately and if needed, the BKOPs will be performed without waiting for the next idle time.
---
This patch is based on the periodic BKOPs implementation in version 8 of "support BKOPS feature for eMMC" patch. The patch was modified to answer the following issues:
- In order to prevent a race condition between going into suspend and starting BKOPs,
the suspend timeout of the host controller is taking into accound in determination of the start time of the delayed work
- Since mmc_start_bkops is called from two contexts now, mmc_claim_host was moved to the beginning of the function
- Also, the check of doing_bkops should be protected when determing if an HPI is needed due to the same reason.
- Starting and canceling the delayed work in each idle caused degradation of iozone performance. Therefore, the delayed work is not started on each idle. The amount of write bytes sent from the last delayed work is the trigger for starting the delayed BKOPs work.
- To prevent degaradtion of iozone performance we also moved the call to mmc_claim_host outside of mmc_stop_bkops and its release is done after issue_fn. This prevents an addition of a full claim and release, that is also done in issue_fn for the first request after idle time.
---
drivers/mmc/card/block.c +4 -0
@@ -1268,6 +1268,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (rq_data_dir (rqc) ==WRITE )) + card->host->bkops_info.wr_bytes_since_last_bkops += +blk_rq_bytes (rqc); + do { if (rqc) { /*
drivers/mmc/card/queue.c +20 -0
@@ -51,6 +51,8 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +bool release_host = false; +struct mmc_host *host = mq->card->host; current->flags |=PF_MEMALLOC ;
@@ -66,13 +68,31 @@ 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 (mq->card->ext_csd.bkops_en && + host->bkops_info.started_delayed_bkops) { + host->bkops_info.started_delayed_bkops = false; + release_host = true; +mmc_claim_host (mq->card->host); +mmc_stop_bkops (mq->card); + } +set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (release_host) { + release_host = false; +mmc_release_host (mq->card->host); + } }else {if (kthread_should_stop ()) {set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (mq->card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);
drivers/mmc/core/core.c +183 -6
245 lines not on this page · raw message
drivers/mmc/core/host.c +24 -0
@@ -27,6 +27,11 @@#include "core.h" #include "host.h" +/* +* A default time for checking the need for non urgent BKOPs once MMC thread +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000#define cls_dev_to_mmc_host (d) container_of(d, struct mmc_host, class_dev)static void mmc_host_classdev_release (struct device *dev)
@@ -336,6 +341,11 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)spin_lock_init (&host->lock);init_waitqueue_head (&host->wq);INIT_DELAYED_WORK (&host->detect, mmc_rescan); + host->bkops_info. wq =create_singlethread_workqueue ("bkops_wq" ); +INIT_DELAYED_WORK (&host->bkops_info.idle_time_dw, + mmc_start_idle_time_bkops); +INIT_WORK (&host->bkops_info.completion_polling, + mmc_bkops_completion_polling);#ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify;#endif
@@ -386,6 +396,20 @@ int mmc_add_host(struct mmc_host *host)#endif mmc_host_clk_sysfs_init (host); +/* +* Calculate the time to start the BKOPs checking. +* The idle time of the host controller should be taken into account +* in order to prevent a race condition before starting BKOPs and +* going into suspend. +* If the host controller didn't set its idle time, a default value is +* used. +*/ + host->bkops_info.time_to_start_bkops_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (host->bkops_info.host_suspend_tout_ms) + host->bkops_info.time_to_start_bkops_ms =min ( + host->bkops_info.time_to_start_bkops_ms, + host->bkops_info.host_suspend_tout_ms/2 ); +mmc_start_host (host);register_pm_notifier (&host->pm_notify);
include/linux/mmc/core.h +3 -0
@@ -145,6 +145,9 @@ extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);extern int mmc_wait_for_app_cmd (struct mmc_host *,struct mmc_card *,struct mmc_command *,int );extern void mmc_start_bkops (struct mmc_card *card,bool from_exception); +extern void mmc_start_delayed_bkops (struct mmc_card *card); +extern void mmc_start_idle_time_bkops (struct work_struct *work); +extern void mmc_bkops_completion_polling (struct work_struct *work);extern int __mmc_switch (struct mmc_card *,u8 ,u8 ,u8 ,unsigned int ,bool );extern int mmc_switch (struct mmc_card *,u8 ,u8 ,u8 ,unsigned int );
include/linux/mmc/host.h +29 -0
@@ -176,6 +176,33 @@ struct mmc_supply {struct regulator *vqmmc;/* Optional Vccq supply */ }; +/** +* struct mmc_bkops_info - BKOPs data +* @wq: workqueue +* @idle_time_dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @time_to_start_bkops_ms: The time to start the BKOPs +* delayed work once MMC thread is idle +* @completion_polling: 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 +* @wr_bytes_since_last_bkops :number of write bytes since the +* last idle BKOPs was scheduled +*/ +struct mmc_bkops_info { +struct workqueue_struct *wq; +struct delayed_work idle_time_dw; +unsigned int host_suspend_tout_ms; +unsigned int time_to_start_bkops_ms; +struct work_struct completion_polling; +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int wr_bytes_since_last_bkops; +}; +struct mmc_host {struct device *parent;struct device class_dev;
@@ -341,6 +368,8 @@ struct mmc_host {unsigned int actual_clock;/* Actual HC clock rate */ +struct mmc_bkops_info bkops_info; +unsigned long private[0 ]____cacheline_aligned ; };
Signature · 4 lines
1.7.6 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation