mmc: core: Add support for idle time BKOPs
Subsystems (2)
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 depends on the following patch:
[PATCH v11] mmc: support BKOPS feature for eMMC
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. Currently the number of issued requests from the last delayed work is the trigger. We still investigate the best trigger for starting the delayed 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 | 3 + drivers/mmc/card/queue.c | 20 +++++ drivers/mmc/core/core.c | 188 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/host.c | 24 ++++++ include/linux/mmc/card.h | 3 + include/linux/mmc/core.h | 3 + include/linux/mmc/host.h | 25 ++++++ 7 files changed, 256 insertions(+), 10 deletions(-)
drivers/mmc/card/block.c +3 -0
@@ -1268,6 +1268,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc) + card->idle_bkops_rw_reqs_nr++; + do { if (rqc) { /*
drivers/mmc/card/queue.c +20 -0
@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +bool release_host = false; current->flags |=PF_MEMALLOC ;
@@ -66,13 +67,32 @@ 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->mqrq_prev->req && + mq->card->ext_csd.bkops_en && + mq->card->idle_bkops_rw_reqs_nr ==0 ) { + 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 +178 -10
238 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/card.h +3 -0
@@ -280,6 +280,9 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +/* num of read/write reqs since last BKOPs delayed work was queued */ +unsigned int idle_bkops_rw_reqs_nr; };/*
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 +25 -0
@@ -176,6 +176,29 @@ 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 +* @exit_wait_on_completion: Exit flag for non blocking BKOPs +*/ +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 exit_wait_on_completion; +}; +struct mmc_host {struct device *parent;struct device class_dev;
@@ -340,6 +363,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.3.3 -- Sent by a consultant of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Quoted 106 lines · Maya Erez
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.Signed-off-by Maya Erez <[email protected]>Signed-off-by Jaehoon Chung <[email protected]>---
This patch depends on the following patch:
[PATCH v11] mmc: support BKOPS feature for eMMCThis 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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould 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. Currently the number of issued requests from the last delayed work is the trigger. We still investigate the best trigger for starting the delayed work.
- To prevent degaradtion of iozone performance we also moved the call tommc_claim_hostoutside ofmmc_stop_bkopsand its release is done afterissue_fn. This prevents an addition of a full claim and release, that is also done inissue_fnfor the first request after idle time.--- drivers/mmc/card/block.c | 3 + drivers/mmc/card/queue.c | 20 +++++ drivers/mmc/core/core.c | 188 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/host.c | 24 ++++++ include/linux/mmc/card.h | 3 + include/linux/mmc/core.h | 3 + include/linux/mmc/host.h | 25 ++++++ 7 files changed, 256 insertions(+), 10 deletions(-)drivers/mmc/card/block.c +3
-0@@ -1268,6 +1268,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc) + card->idle_bkops_rw_reqs_nr++; + do { if (rqc) { /*drivers/mmc/card/queue.c +20
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +bool release_host = false; current->flags |=PF_MEMALLOC ;@@ -66,13 +67,32 @@ 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->mqrq_prev->req && + mq->card->ext_csd.bkops_en && + mq->card->idle_bkops_rw_reqs_nr ==0 ) { + 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 +0
-0@@ -46,6 +46,15 @@* operations the card has to perform */
drivers/mmc/core/core.c line 57
Quoted 10 lines
drivers/mmc/core/core.c +9
-0#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000)/* max time to wait in ms */ +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */ +#define BKOPS_COMPLETION_POLLING_TIMEOUT 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL 1000/* in ms */ +/* +* Since canceling the delayed work might have significant effect on the +* performance of small requests we won't queue the delayed work every time +* mmcqd thread is idle +*/ +#define BKOPS_MIN_REQS_TO_QUEUE_DELAYED_WORK 1000
How did you get the minimum request number "1000"? Is this tunable value?
Quoted 6 lines
drivers/mmc/core/core.c +0
-0static struct workqueue_struct *workqueue;static const unsigned freqs[] = {400000 ,300000 ,200000 ,100000 };@@ -252,6 +261,37 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }
drivers/mmc/core/core.c mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) · line 272
Quoted 10 lines
drivers/mmc/core/core.c +9
-0/** +* mmc_start_delayed_bkops() - Start a delayed work to check for the need +* of non urgent BKOPs +* +* @card: MMC card to start BKOPS +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ;
if mmc_card_doing_bkops or didn't support bkops, idle_bkops_rw_reqs_nr need to reset?
drivers/mmc/core/core.c mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) · line 278
Quoted 6 lines
drivers/mmc/core/core.c +6
-0+ +if (card->idle_bkops_rw_reqs_nr <BKOPS_MIN_REQS_TO_QUEUE_DELAYED_WORK ) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work" , __func__, +mmc_hostname (card->host));
missed "\n"..almost missed the new line("\n") at pr_debug.
Quoted 131 lines
drivers/mmc/core/core.c +93
-10+ + card->idle_bkops_rw_reqs_nr =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the queue_thread and the delayed work +*/ + card->host->bkops_info.cancel_delayed_work = false; +queue_delayed_work (card->host->bkops_info. wq, + &card->host->bkops_info.idle_time_dw, +msecs_to_jiffies ( + card->host->bkops_info.time_to_start_bkops_ms)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS * @form_exception: A flags to indicate if this function was @@ -268,23 +308,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->host->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit" , + __func__,mmc_hostname (card->host)); + card->host->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit" , __func__, +mmc_hostname (card->host)); + goto out; + } + err =mmc_read_bkops_status (card);if (err) { -pr_err ("%s: Didn't read bkops status : %d\n" , +pr_err ("%s: Error %d while reading bkops status\n" ,mmc_hostname (card->host), err); -return ; + goto out; } -if (!card->ext_csd.raw_bkops_status) -return ; + goto out; -if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 - && (from_exception)) -return ; +pr_info ("%s: %s: card->ext_csd.raw_bkops_status = %d" , __func__, +mmc_hostname (card->host), 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 ((card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 ) + && (from_exception)) { +pr_debug ("%s: %s: Level 1 from exception, exit" , __func__, +mmc_hostname (card->host)); + goto out; + }mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) {@@ -308,13 +372,101 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)* bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +if (!use_busy_signal) {mmc_card_set_doing_bkops (card); +pr_debug ("%s: %s: starting the polling thread" , __func__, +mmc_hostname (card->host)); + card->host->bkops_info.exit_wait_on_completion = false; +queue_work (system_nrt_wq, + &card->host->bkops_info.completion_polling); + } out:mmc_release_host (card->host); + }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPs completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPs. +* Since the non blocking BKOPs can be interrupted by a fetched +* request we also check exit_wait_on_completion. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_host *host = container_of(work, struct mmc_host, + bkops_info.completion_polling); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ + do { + mmc_claim_host(host); + + if (host->bkops_info.exit_wait_on_completion || + (!mmc_card_doing_bkops(host->card))) { + goto out; + } + + err = mmc_send_status(host->card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(host), err);
drivers/mmc/core/core.c void mmc_start_bkops(struct mmc_card *card, bool from_exception) · line 434
Quoted 10 lines
drivers/mmc/core/core.c +10
-0+ goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +pr_debug ("%s: completed BKOPs, exit polling" , __func__);
missing "..\n". other pr_debug also missed.
Quoted 188 lines
drivers/mmc/core/core.c +51
-0+mmc_card_clr_doing_bkops (host->card); + goto out; + } + +mmc_release_host (host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_debug ("%s: exit polling due to timeout" , __func__); + +return ; +out: +mmc_release_host (host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPs is +* needed +* @work: The idle time BKOPs work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_host *host = container_of(work, struct mmc_host, + bkops_info.idle_time_dw.work); + +mmc_start_bkops (host->card, false); + +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -574,12 +726,28 @@ EXPORT_SYMBOL(mmc_wait_for_cmd);* 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. +* +* This function should be called when the host is claimed */ int mmc_stop_bkops (struct mmc_card *card) {int err =0 ;BUG_ON (!card); + +if (delayed_work_pending (&card->host->bkops_info.idle_time_dw)) +cancel_delayed_work_sync (&card->host->bkops_info.idle_time_dw); + +/* +* Notify the delayed work to be cancelled, in case it was already +* removed from the queue, but was not started yet +*/ + card->host->bkops_info.cancel_delayed_work = true; + +if (!mmc_card_doing_bkops (card)) +return err; + + card->host->bkops_info.exit_wait_on_completion = true; err =mmc_interrupt_hpi (card);/* 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/card.h +3
-0@@ -280,6 +280,9 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +/* num of read/write reqs since last BKOPs delayed work was queued */ +unsigned int idle_bkops_rw_reqs_nr; };/* 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 +25
-0@@ -176,6 +176,29 @@ 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 +* @exit_wait_on_completion: Exit flag for non blocking BKOPs +*/ +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 exit_wait_on_completion; +}; +struct mmc_host {struct device *parent;struct device class_dev;@@ -340,6 +363,8 @@ struct mmc_host {unsigned int actual_clock;/* Actual HC clock rate */ +struct mmc_bkops_info bkops_info; +unsigned long private[0 ]____cacheline_aligned ; };
Quoted 127 lines · Jaehoon Chung
Quoted 126 lines · Maya Erez
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.Signed-off-by Maya Erez <[email protected]>Signed-off-by Jaehoon Chung <[email protected]>---
This patch depends on the following patch:
[PATCH v11] mmc: support BKOPS feature for eMMCThis 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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould 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. Currently the number of
issued requests from the last delayed work
is the trigger. We still investigate the best trigger for starting the
delayed work.
- To prevent degaradtion of iozone performance we also moved the call tommc_claim_hostoutside ofmmc_stop_bkops
and its release is done afterissue_fn. This prevents an addition of a
full claim and release, that is also done
inissue_fnfor the first request after idle time.--- drivers/mmc/card/block.c | 3 + drivers/mmc/card/queue.c | 20 +++++ drivers/mmc/core/core.c | 188 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/host.c | 24 ++++++ include/linux/mmc/card.h | 3 + include/linux/mmc/core.h | 3 + include/linux/mmc/host.h | 25 ++++++ 7 files changed, 256 insertions(+), 10 deletions(-)drivers/mmc/card/block.c +3
-0@@ -1268,6 +1268,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc) + card->idle_bkops_rw_reqs_nr++; + do { if (rqc) {drivers/mmc/card/block.c +0
-0/* drivers/mmc/card/queue.c +20
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +bool release_host = false; current->flags |=PF_MEMALLOC ;@@ -66,13 +67,32 @@ 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->mqrq_prev->req && + mq->card->ext_csd.bkops_en && + mq->card->idle_bkops_rw_reqs_nr ==0 ) { + 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 +1
-0@@ -46,6 +46,15 @@* operations the card has to perform */ #define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000)/* max time to wait in ms */ +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
drivers/mmc/core/core.c +4
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL 1000/* in ms */ +/* +* Since canceling the delayed work might have significant effect on
Quoted 8 lines
Quoted 6 lines
the
+ * performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle
+ */
+#defineBKOPS_MIN_REQS_TO_QUEUE_DELAYED_WORK1000How did you get the minimum request number "1000"? Is this tunable value?
We tried several values to prevent degradation of iozone results and this gave the best results.
We are still tuning this value and one of the options is to count the number of write requests only (since read activity on the card won't change the need for BKOPs).
Quoted 20 lines
Quoted 14 lines
static struct
workqueue_struct*workqueue;
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };drivers/mmc/core/core.c +1
-0@@ -252,6 +261,37 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for the need
+ * of non urgent BKOPs
+ *
+ * @card: MMC card to start BKOPS
+ */drivers/mmc/core/core.c mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) · line 272
Quoted 4 lines
drivers/mmc/core/core.c +4
-0+void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ;if
mmc_card_doing_bkopsor didn't support bkops,idle_bkops_rw_reqs_nrneed to reset?
Quoted 8 lines
drivers/mmc/core/core.c mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) · line 278
Quoted 7 lines
drivers/mmc/core/core.c +2
-0+ + if (card->idle_bkops_rw_reqs_nr <
BKOPS_MIN_REQS_TO_QUEUE_DELAYED_WORK)drivers/mmc/core/core.c +4
-0+return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work" , __func__, +mmc_hostname (card->host));missed "\n"..almost missed the new
line("\n")atpr_debug.
Will fix that.
Quoted 136 lines
Quoted 136 lines
drivers/mmc/core/core.c +49
-9+ + card->idle_bkops_rw_reqs_nr =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the queue_thread and the delayed work +*/ + card->host->bkops_info.cancel_delayed_work = false; +queue_delayed_work (card->host->bkops_info. wq, + &card->host->bkops_info.idle_time_dw, +msecs_to_jiffies ( + card->host->bkops_info.time_to_start_bkops_ms)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS * @form_exception: A flags to indicate if this function was @@ -268,23 +308,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->host->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit" , + __func__,mmc_hostname (card->host)); + card->host->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit" , __func__, +mmc_hostname (card->host)); + goto out; + } + err =mmc_read_bkops_status (card);if (err) { -pr_err ("%s: Didn't read bkops status : %d\n" , +pr_err ("%s: Error %d while reading bkops status\n" ,mmc_hostname (card->host), err); -return ; + goto out; } -if (!card->ext_csd.raw_bkops_status) -return ; + goto out; -if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 - && (from_exception)) -return ; +pr_info ("%s: %s: card->ext_csd.raw_bkops_status = %d" , __func__, +mmc_hostname (card->host), 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 ((card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 ) + && (from_exception)) { +pr_debug ("%s: %s: Level 1 from exception, exit" , __func__, +mmc_hostname (card->host)); + goto out; + }mmc_claim_host (card->host);drivers/mmc/core/core.c +47
-1if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) {@@ -308,13 +372,101 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +if (!use_busy_signal) {mmc_card_set_doing_bkops (card); +pr_debug ("%s: %s: starting the polling thread" , __func__, +mmc_hostname (card->host)); + card->host->bkops_info.exit_wait_on_completion = false; +queue_work (system_nrt_wq, + &card->host->bkops_info.completion_polling); + } out:mmc_release_host (card->host); + }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPs completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPs. +* Since the non blocking BKOPs can be interrupted by a fetched +* request we also check exit_wait_on_completion. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_host *host = container_of(work, struct mmc_host, + bkops_info.completion_polling); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ + do { + mmc_claim_host(host); + + if (host->bkops_info.exit_wait_on_completion || + (!mmc_card_doing_bkops(host->card))) { + goto out; + } + + err = mmc_send_status(host->card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(host), err); + goto out; + } +
Quoted 8 lines
drivers/mmc/core/core.c void mmc_start_bkops(struct mmc_card *card, bool from_exception) · line 434
Quoted 7 lines
drivers/mmc/core/core.c +7
-0+/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +pr_debug ("%s: completed BKOPs, exit polling" , __func__);missing "..\n". other
pr_debugalso missed.
Will fix that.
Quoted 188 lines
Quoted 188 lines
drivers/mmc/core/core.c +35
-0+mmc_card_clr_doing_bkops (host->card); + goto out; + } + +mmc_release_host (host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_debug ("%s: exit polling due to timeout" , __func__); + +return ; +out: +mmc_release_host (host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPs is +* needed +* @work: The idle time BKOPs work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_host *host = container_of(work, struct mmc_host, + bkops_info.idle_time_dw.work); + +mmc_start_bkops (host->card, false); + +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {drivers/mmc/core/core.c +16
-0complete (&mrq->completion);@@ -574,12 +726,28 @@ EXPORT_SYMBOL(mmc_wait_for_cmd);* 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. +* +* This function should be called when the host is claimed */ int mmc_stop_bkops (struct mmc_card *card) {int err =0 ;BUG_ON (!card); + +if (delayed_work_pending (&card->host->bkops_info.idle_time_dw)) +cancel_delayed_work_sync (&card->host->bkops_info.idle_time_dw); + +/* +* Notify the delayed work to be cancelled, in case it was already +* removed from the queue, but was not started yet +*/ + card->host->bkops_info.cancel_delayed_work = true; + +if (!mmc_card_doing_bkops (card)) +return err; + + card->host->bkops_info.exit_wait_on_completion = true; err =mmc_interrupt_hpi (card);/* drivers/mmc/core/host.c +2
-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.
+ */
+#defineMMC_IDLE_BKOPS_TIME_MS2000
#definecls_dev_to_mmc_host(d)container_of(d, structmmc_host,class_dev)static void
mmc_host_classdev_release(struct device *dev)drivers/mmc/core/host.c +5
-0@@ -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
drivers/mmc/core/host.c +14
-0@@ -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/card.h +3
-0@@ -280,6 +280,9 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +/* num of read/write reqs since last BKOPs delayed work was queued */ +unsigned int idle_bkops_rw_reqs_nr; };/* 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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);include/linux/mmc/host.h +23
-0@@ -176,6 +176,29 @@ 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 +* @exit_wait_on_completion: Exit flag for non blocking BKOPs +*/ +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 exit_wait_on_completion; +}; +struct mmc_host {struct device *parent;struct device class_dev;@@ -340,6 +363,8 @@ struct mmc_host {unsigned int actual_clock;/* Actual HC clock rate */
Quoted 12 lines
include/linux/mmc/host.h struct mmc_host { · line 370
Quoted 6 lines
include/linux/mmc/host.h +2
-0+struct mmc_bkops_info bkops_info; +unsigned long private[0 ]____cacheline_aligned ; };--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Thanks,
Maya
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v2 1/1 mmc: core: Add support for idle time BKOPS
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updating BKOPS_STATUS (EXT_CSD byte [246]).
According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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 sectors changed (written or discard) from the last delayed work is the trigger for starting the delayed BKOPS work.
---
drivers/mmc/card/block.c +6 -0
@@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) from =blk_rq_pos (req); nr =blk_rq_sectors (req); +if (card->ext_csd.bkops_en) + card->bkops_info.sectors_changed +=blk_rq_sectors (req); +if (mmc_can_discard (card)) arg =MMC_DISCARD_ARG ;else if (mmc_can_trim (card))
@@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir (rqc) ==WRITE )) + card->bkops_info.sectors_changed +=blk_rq_sectors (rqc); + do { if (rqc) { /*
drivers/mmc/card/queue.c +13 -0
@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;
@@ -66,6 +67,17 @@ 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 {
@@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);
drivers/mmc/core/core.c +174 -11
248 lines not on this page · raw message
drivers/mmc/core/mmc.c +24 -0
@@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + + } + } +if (!oldcard) host->card = card;
include/linux/mmc/card.h +44 -0
56 lines not on this page · raw message
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 );
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
Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
Hi Chris and all,
According to the eMMC4.5 standard, a host that enables the BKOPS_EN bit must also check the BKOPS status periodically:
"Host shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations, to help reduce the latencies during foreground operations. If the status is at level 3 ("critical"), some operations may extend beyond their original timeouts due to maintenance operations which cannot be delayed anymore. The host should give the device enough time for background operations to avoid getting to this level in the first place."
As mentioned in the standard quotation above, it is not recommended to handle only BKOPS of level 3 since it can lead to foreground operations timeout.
I would appreciate if you could review this change to add the periodic BKOPS on top of handling BKOPS level 3 that was already mainlined.
Thnaks,
Maya
Quoted 503 lines · Maya Erez
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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.Signed-off-by Maya Erez <[email protected]>Signed-off-by Jaehoon Chung <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould 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 sectors
changed (written or discard) from the last
delayed work is the trigger for starting the delayed BKOPS work.
---drivers/mmc/card/block.c +3
-0@@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) from =blk_rq_pos (req); nr =blk_rq_sectors (req); +if (card->ext_csd.bkops_en) + card->bkops_info.sectors_changed +=blk_rq_sectors (req); +if (mmc_can_discard (card)) arg =MMC_DISCARD_ARG ;drivers/mmc/card/block.c +3
-0else if (mmc_can_trim (card))@@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir (rqc) ==WRITE )) + card->bkops_info.sectors_changed +=blk_rq_sectors (rqc); + do { if (rqc) {drivers/mmc/card/block.c +0
-0/* drivers/mmc/card/queue.c +13
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -66,6 +67,17 @@ 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 {@@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +34
-1@@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +if (card->bkops_info.sectors_changed < +BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK ) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + + card->bkops_info.sectors_changed =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested.
drivers/mmc/core/core.c +28
-6@@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ;drivers/mmc/core/core.c +96
-1use_busy_signal = true;@@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {drivers/mmc/core/core.c +16
-3complete (&mrq->completion);@@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;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 +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out: +mmc_release_host (card->host);return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { err =mmc_stop_bkops (host->card);if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }drivers/mmc/core/core.c +0
-0spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +24
-0@@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +26
-0@@ -207,6 +207,48 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
include/linux/mmc/card.h +8
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* 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 +* performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle.
+ * The delayed work for idle BKOPS will be scheduled only after a significant
+ * amount of write or discard data.
+ * 100MB is chosen based on benchmark tests.
+ */include/linux/mmc/card.h +5
-0+#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800/* 100MB */ +}; +/* * MMC device */ @@ -281,6 +323,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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);--
1.7.6
--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
In reply to Maya ErezHi Maya,
I'm testing with your patch..but i need to have the more time for testing. In now, it looks good to me. Thank you for working the idle bkops.
Best Regards,
Jaehoon Chung
drivers/mmc/card/block.c static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) · line 831
Quoted 11 lines
---
drivers/mmc/card/block.c +2
-0@@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) from =blk_rq_pos (req); nr =blk_rq_sectors (req); +if (card->ext_csd.bkops_en) + card->bkops_info.sectors_changed +=blk_rq_sectors (req);
using nr?
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1275
Quoted 10 lines
drivers/mmc/card/block.c +3
-0+if (mmc_can_discard (card)) arg =MMC_DISCARD_ARG ;else if (mmc_can_trim (card))@@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir (rqc) ==WRITE )) + card->bkops_info.sectors_changed +=blk_rq_sectors (rqc);
Fix the indent.
Quoted 19 lines
drivers/mmc/card/block.c +1
-0+ do { if (rqc) { /*drivers/mmc/card/queue.c +1
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -66,6 +67,17 @@ static int mmc_queue_thread(void *d)spin_unlock_irq (q->queue_lock);
drivers/mmc/card/queue.c static int mmc_queue_thread(void *d) · line 78
Quoted 10 lines
drivers/mmc/card/queue.c +9
-0if (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);
if mmc_stop_bkops is failed..?
Quoted 383 lines
drivers/mmc/card/queue.c +3
-0+ } +set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); }else {@@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +174
-11@@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +if (card->bkops_info.sectors_changed < +BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK ) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + + card->bkops_info.sectors_changed =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)* bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;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 +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out: +mmc_release_host (card->host);return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block *notify_block,switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { err =mmc_stop_bkops (host->card);if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +24
-0@@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +44
-0@@ -207,6 +207,48 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */ +#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* 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 +* performance of small requests we won't queue the delayed work every time +* mmcqd thread is idle. +* The delayed work for idle BKOPS will be scheduled only after a significant +* amount of write or discard data. +* 100MB is chosen based on benchmark tests. +*/ +#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800/* 100MB */ +}; +/* * MMC device */ @@ -281,6 +323,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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 );
Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
Hi Jaehoon,
Any update on this patch review and testing?
Thanks,
Maya
Quoted 460 lines · Jaehoon Chung
Hi Maya,
I'm testing with your patch..but i need to have the more time for testing. In now, it looks good to me. Thank you for working the idle bkops.
Best Regards,
Jaehoon Chungdrivers/mmc/card/block.c static int mmc_blk_issue_discard_rq(struct mmc_queue · line 832
Quoted 12 lines
---
drivers/mmc/card/block.c +2
-0@@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) from =blk_rq_pos (req); nr =blk_rq_sectors (req); +if (card->ext_csd.bkops_en) + card->bkops_info.sectors_changed +=blk_rq_sectors (req);using nr?
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue · line 1276
Quoted 11 lines
drivers/mmc/card/block.c +1
-0+if (mmc_can_discard (card)) arg =MMC_DISCARD_ARG ;drivers/mmc/card/block.c +2
-0else if (mmc_can_trim (card))@@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir (rqc) ==WRITE )) + card->bkops_info.sectors_changed +=blk_rq_sectors (rqc);Fix the indent.
Quoted 19 lines
drivers/mmc/card/block.c +1
-0+ do { if (rqc) {drivers/mmc/card/block.c +0
-0/* drivers/mmc/card/queue.c +1
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -66,6 +67,17 @@ static int mmc_queue_thread(void *d)spin_unlock_irq (q->queue_lock);drivers/mmc/card/queue.c static int mmc_queue_thread(void *d) · line 78
Quoted 10 lines
drivers/mmc/card/queue.c +9
-0if (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);if
mmc_stop_bkopsis failed..?Quoted 395 lines
drivers/mmc/card/queue.c +3
-0+ } +set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); }else {@@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +34
-1@@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +if (card->bkops_info.sectors_changed < +BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK ) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + + card->bkops_info.sectors_changed =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested.
drivers/mmc/core/core.c +28
-6@@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ;drivers/mmc/core/core.c +96
-1use_busy_signal = true;@@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {drivers/mmc/core/core.c +16
-3complete (&mrq->completion);@@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;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 +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out: +mmc_release_host (card->host);return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { err =mmc_stop_bkops (host->card);if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }drivers/mmc/core/core.c +0
-0spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +24
-0@@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +26
-0@@ -207,6 +207,48 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
include/linux/mmc/card.h +7
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* 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
+ * performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle.
+ * The delayed work for idle BKOPS will be scheduled only after a significant
+ * amount of write or discard data.
+ * 100MB is chosen based on benchmark tests.
+ */include/linux/mmc/card.h +5
-0+#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800/* 100MB */ +}; +/* * MMC device */ @@ -281,6 +323,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Jaehoon Chung
Tested-byRe: [PATCH v2] mmc: core: Add support for idle time BKOPS
Hi Maya,
I tested with dw-mmc controller.
It's look good to me.
Quoted 469 lines · On 11/07/2012 01:28 PM, [email protected] wrote:
Hi Jaehoon,
Any update on this patch review and testing?
Thanks,
MayaQuoted 460 lines · Jaehoon Chung
Hi Maya,
I'm testing with your patch..but i need to have the more time for testing. In now, it looks good to me. Thank you for working the idle bkops.
Best Regards,
Jaehoon Chungdrivers/mmc/card/block.c static int mmc_blk_issue_discard_rq(struct mmc_queue · line 832
Quoted 12 lines
---
drivers/mmc/card/block.c +2
-0@@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) from =blk_rq_pos (req); nr =blk_rq_sectors (req); +if (card->ext_csd.bkops_en) + card->bkops_info.sectors_changed +=blk_rq_sectors (req);using nr?
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue · line 1276
Quoted 11 lines
drivers/mmc/card/block.c +1
-0+if (mmc_can_discard (card)) arg =MMC_DISCARD_ARG ;drivers/mmc/card/block.c +2
-0else if (mmc_can_trim (card))@@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir (rqc) ==WRITE )) + card->bkops_info.sectors_changed +=blk_rq_sectors (rqc);Fix the indent.
Quoted 19 lines
drivers/mmc/card/block.c +1
-0+ do { if (rqc) {drivers/mmc/card/block.c +0
-0/* drivers/mmc/card/queue.c +1
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -66,6 +67,17 @@ static int mmc_queue_thread(void *d)spin_unlock_irq (q->queue_lock);drivers/mmc/card/queue.c static int mmc_queue_thread(void *d) · line 78
Quoted 10 lines
drivers/mmc/card/queue.c +9
-0if (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);if
mmc_stop_bkopsis failed..?Quoted 395 lines
drivers/mmc/card/queue.c +3
-0+ } +set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); }else {@@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +34
-1@@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +if (card->bkops_info.sectors_changed < +BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK ) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + + card->bkops_info.sectors_changed =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested.
drivers/mmc/core/core.c +28
-6@@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ;drivers/mmc/core/core.c +96
-1use_busy_signal = true;@@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {drivers/mmc/core/core.c +16
-3complete (&mrq->completion);@@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;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 +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out: +mmc_release_host (card->host);return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { err =mmc_stop_bkops (host->card);if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }drivers/mmc/core/core.c +0
-0spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +24
-0@@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +26
-0@@ -207,6 +207,48 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
include/linux/mmc/card.h +7
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* 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
+ * performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle.
+ * The delayed work for idle BKOPS will be scheduled only after a significant
+ * amount of write or discard data.
+ * 100MB is chosen based on benchmark tests.
+ */include/linux/mmc/card.h +5
-0+#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800/* 100MB */ +}; +/* * MMC device */ @@ -281,6 +323,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
In reply to Maya ErezThanks Maya. Looks good to me.
Regards,
Subhash
Quoted 474 lines · Maya Erez
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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.Signed-off-by Maya Erez <[email protected]>Signed-off-by Jaehoon Chung <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould 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 sectors changed (written or discard) from the last delayed work is the trigger for starting the delayed BKOPS work.
---drivers/mmc/card/block.c +6
-0@@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) from =blk_rq_pos (req); nr =blk_rq_sectors (req); +if (card->ext_csd.bkops_en) + card->bkops_info.sectors_changed +=blk_rq_sectors (req); +if (mmc_can_discard (card)) arg =MMC_DISCARD_ARG ;else if (mmc_can_trim (card))@@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)if (!rqc && !mq->mqrq_prev->req)return 0 ; +if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir (rqc) ==WRITE )) + card->bkops_info.sectors_changed +=blk_rq_sectors (rqc); + do { if (rqc) { /*drivers/mmc/card/queue.c +13
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -66,6 +67,17 @@ 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 {@@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +174
-11@@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +if (card->bkops_info.sectors_changed < +BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK ) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + + card->bkops_info.sectors_changed =0 ; + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)* bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;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 +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out: +mmc_release_host (card->host);return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block *notify_block,switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { err =mmc_stop_bkops (host->card);if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +24
-0@@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +44
-0@@ -207,6 +207,48 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */ +#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* 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 +* performance of small requests we won't queue the delayed work every time +* mmcqd thread is idle. +* The delayed work for idle BKOPS will be scheduled only after a significant +* amount of write or discard data. +* 100MB is chosen based on benchmark tests. +*/ +#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800/* 100MB */ +}; +/* * MMC device */ @@ -281,6 +323,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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 );
PATCH 1/6 mmc: core: sdio_io.c: Fixed lines with > 80 chars
I fixed lines over 80 characters per line.
--- drivers/mmc/core/sdio_io.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/mmc/core/sdio_io.c +4 -2
@@ -80,7 +80,8 @@ int sdio_enable_func(struct sdio_func *func) timeout = jiffies +msecs_to_jiffies (func->enable_timeout);while (1 ) { - ret =mmc_io_rw_direct (func->card,0 ,0 , SDIO_CCCR_IORx,0 , ®); + ret =mmc_io_rw_direct (func->card,0 ,0 , SDIO_CCCR_IORx,0 , + ®);if (ret) goto err;if (reg & (1 << func->num))
@@ -664,7 +665,8 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,BUG_ON (!func); -if ((addr <0xF0 || addr >0xFF ) && (!mmc_card_lenient_fn0 (func->card))) { +if ((addr <0xF0 || addr >0xFF ) && + (!mmc_card_lenient_fn0 (func->card))) {if (err_ret) *err_ret = -EINVAL ;return ;
Signature · 1 line
1.7.9.5
PATCH 2/6 mmc: core: sdio_bus.c: Removed trailing whitespace error
Removed the one unnecessary whitespace error from sdio_bus.c
--- drivers/mmc/core/sdio_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mmc/core/sdio_bus.c +1 -1
@@ -105,7 +105,7 @@ sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env) "SDIO_CLASS=%02X", func->class))return -ENOMEM ; -if (add_uevent_var (env, +if (add_uevent_var (env,"SDIO_ID=%04X:%04X" , func->vendor, func->device))return -ENOMEM ;
Signature · 1 line
1.7.9.5
PATCH 3/6 mmc: core: sdio_bus.c: Fixed lines > 80 chars
In reply to Sangho YiI fixed lines over 80 characters per line.
--- drivers/mmc/core/sdio_bus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/mmc/core/sdio_bus.c +3 -2
@@ -27,7 +27,7 @@/* show configuration fields */ #define sdio_config_attr (field, format_string) \ static ssize_t \ -field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf)\ { \ struct sdio_func *func; \ \
@@ -39,7 +39,8 @@ sdio_config_attr(class, "0x%02x\n");sdio_config_attr (vendor,"0x%04x\n" );sdio_config_attr (device,"0x%04x\n" ); -static ssize_t modalias_show (struct device *dev,struct device_attribute *attr,char *buf) +static ssize_t modalias_show (struct device *dev,struct device_attribute *attr, +char *buf) {struct sdio_func *func =dev_to_sdio_func (dev);
Signature · 1 line
1.7.9.5
Re: [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars
Hi,
your patch[1/3] is also "Fixed lines > 80chars". Why do you separate with patch[1/6] and patch[3/6]?
Best Regards,
Jaehoon Chung
Quoted 31 lines · Sangho Yi
I fixed lines over 80 characters per line.
Signed-off-by Sangho Yi <[email protected]>--- drivers/mmc/core/sdio_bus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)drivers/mmc/core/sdio_bus.c +3
-2@@ -27,7 +27,7 @@/* show configuration fields */ #define sdio_config_attr (field, format_string) \ static ssize_t \ -field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf)\ { \ struct sdio_func *func; \ \@@ -39,7 +39,8 @@ sdio_config_attr(class, "0x%02x\n");sdio_config_attr (vendor,"0x%04x\n" );sdio_config_attr (device,"0x%04x\n" ); -static ssize_t modalias_show (struct device *dev,struct device_attribute *attr,char *buf) +static ssize_t modalias_show (struct device *dev,struct device_attribute *attr, +char *buf) {struct sdio_func *func =dev_to_sdio_func (dev);
PATCH 4/6 mmc: core: sdio_bus.c: Removed space between func name and ()
In reply to Sangho YiFixed a coding style warning like this:
from foo (arg) --> to foo(arg)
--- drivers/mmc/core/sdio_bus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/mmc/core/sdio_bus.c +3 -3
@@ -31,8 +31,8 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf)\ { \struct sdio_func *func; \ \ - func =dev_to_sdio_func (dev); \ -return sprintf (buf, format_string, func->field); \ + func =dev_to_sdio_func (dev); \ +return sprintf (buf, format_string, func->field); \ }sdio_config_attr (class,"0x%02x\n" );
@@ -42,7 +42,7 @@ sdio_config_attr(device, "0x%04x\n");static ssize_t modalias_show (struct device *dev,struct device_attribute *attr,char *buf) { -struct sdio_func *func =dev_to_sdio_func (dev); +struct sdio_func *func =dev_to_sdio_func (dev);return sprintf (buf,"sdio:c%02Xv%04Xd%04X\n" , func->class, func->vendor, func->device);
Signature · 1 line
1.7.9.5
PATCH 5/6 mmc: core: sdio_bus.c: Fixed a warning for the pr_warning(...
In reply to Sangho YiI fixed a line which had the following warning:
WARNING: Prefer pr_warn(... to pr_warning(...
--- drivers/mmc/core/sdio_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mmc/core/sdio_bus.c +1 -1
@@ -175,7 +175,7 @@ static int sdio_bus_remove(struct device *dev) drv->remove (func);if (func->irq_handler) { -pr_warning ("WARNING: driver %s did not remove " +pr_warn ("WARNING: driver %s did not remove " "its interrupt handler!\n" , drv->name);sdio_claim_host (func);sdio_release_irq (func);
Signature · 1 line
1.7.9.5
PATCH 6/6 mmc: core: sdio_bus.c: Removed unnecessary NULL check routine
In reply to Sangho Yikfree(something) is safe function, so I removed the NULL check routine for the input of kfree.
--- drivers/mmc/core/sdio_bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/mmc/core/sdio_bus.c +1 -2
@@ -259,8 +259,7 @@ static void sdio_release_func(struct device *dev)sdio_free_func_cis (func); -if (func->info) -kfree (func->info); +kfree (func->info);kfree (func); }
Signature · 1 line
1.7.9.5
Re: [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars
In reply to Sangho YiQuoted 1 line · Sangho Yi
I fixed lines over 80 characters per line.
If you really must do these things, please use checkpatch.pl --strict for guidance on how to align function arguments.
drivers/mmc/core/sdio_io.c
Quoted 1 line
drivers/mmc/core/sdio_io.c +0
-0
[]
drivers/mmc/core/sdio_io.c void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, · line 669
Quoted 7 lines
drivers/mmc/core/sdio_io.c +2
-1@@ -664,7 +665,8 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,BUG_ON (!func); -if ((addr <0xF0 || addr >0xFF ) && (!mmc_card_lenient_fn0 (func->card))) { +if ((addr <0xF0 || addr >0xFF ) && + (!mmc_card_lenient_fn0 (func->card))) {
ugly alignment and unnecessary parenthesis too.
PATCH v2 1/1 mmc: fix async request mechanism for sequential read scenarios
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.
With this change read throughput is improved by 16%.
--- drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)
drivers/mmc/card/block.c +11 -15
@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );
@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }
@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;
@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/*
@@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, (int *)&status); } }while (ret);
@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */
@@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;
drivers/mmc/card/queue.c +23 -3
@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff.
@@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request
@@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }
@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );
drivers/mmc/card/queue.h +3 -0
@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;
@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
drivers/mmc/core/core.c +99 -3
137 lines not on this page · raw message
include/linux/mmc/card.h +12 -0
@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */
include/linux/mmc/core.h +15 -0
@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;
@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ;
Signature · 1 line
1.7.6
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
Hi Konstantin,
Quoted 7 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
Quoted 7 lines
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
Quoted 3 lines
Signed-off-by Konstantin Dorfman <[email protected]>---
Please add a change log here with a brief description of the changes since last version
Quoted 377 lines
drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)drivers/mmc/card/block.c +11
-15@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/* @@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, (int *)&status); } }while (ret);@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ @@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;drivers/mmc/card/queue.c +23
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +99
-3@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,57 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + context_info->is_waiting_last_req = false; +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -423,8 +506,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG_ON (1 ); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +531,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +547,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +15
-1@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ; --1.7.6
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
Hi Konstantin,
Quoted 10 lines · Per Förlin
Hi Konstantin,
Quoted 7 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
Quoted 8 lines
Quoted 7 lines
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
I also want to know which benchmark use?
If you can share it, i will test with yours.
Best Regards,
Jaehoon Chung
Quoted 389 lines
Quoted 4 lines
Signed-off-by Konstantin Dorfman <[email protected]>---
Please add a change log here with a brief description of the changes since last version
Quoted 375 lines
drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)drivers/mmc/card/block.c +11
-15@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/* @@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, (int *)&status); } }while (ret);@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ @@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;drivers/mmc/card/queue.c +23
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +99
-3@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,57 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + context_info->is_waiting_last_req = false; +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -423,8 +506,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG_ON (1 ); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +531,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +547,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +15
-1@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ; --Quoted 2 lines
1.7.6
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
On 11/05/2012 09:15 AM, Jaehoon Chung wrote:
Hello,
Quoted 18 lines
Hi Konstantin,
Quoted 16 lines · Per Förlin
Hi Konstantin,
Quoted 13 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.
Quoted 7 lines
Quoted 2 lines
Quoted 1 line
With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
I also want to know which benchmark use?
If you can share it, i will test with yours.Best Regards,
Jaehoon Chung
Our tests were done using lmdd and tiotest and iozone:
TIOTEST
-------
tiotest seq: ./data/tiotest -t 1 -d /data/mmc0 -f 800 -b $((512*1024)) -k 1 -k 3
tiotest rand: ./data/tiotest -t 4 -d /data/mmc0 -f 800 -b 4096 -k 2 -k 0 -p -s 2012 -r 12500 -n 20
LMDD
----
lmdd write: ./data/lmdd if=internal of=/data/mmc0/push1 bs=128k count=2000 sync=1
lmdd read: ./data/lmdd of=internal if=/data/mmc0/push1 bs=128k count=2000
IOZONE
------
iozone (worst): ./data/iozone -i0 -i2 -r4k -s100m -O -o -I -f /data/mmc0/file3
iozone (best): ./data/iozone -i0 -i2 -r4k -s100m -O -I -f /data/mmc0/file3
Thanks,
Signature · 4 lines
Konstantin Dorfman, QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
In reply to Per FörlinHi,
I tested with this patch.
But i got some problem. So i want to get your opinion. I used eMMC4.5 card, and using ddr mode, dw-mmc controller. Also use the post/pre-request() in controller.
Then i got this message every time.
[ 7.735520] mmc0: new request while areq = eda3046c
What's wrong?
Best Regards,
Jaehoon Chung
Quoted 407 lines · Per Förlin
Hi Konstantin,
Quoted 7 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
Quoted 7 lines
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
Quoted 3 lines
Signed-off-by Konstantin Dorfman <[email protected]>---
Please add a change log here with a brief description of the changes since last version
Quoted 375 lines
drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)drivers/mmc/card/block.c +11
-15@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/* @@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, (int *)&status); } }while (ret);@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ @@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;drivers/mmc/card/queue.c +23
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +99
-3@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,57 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + context_info->is_waiting_last_req = false; +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -423,8 +506,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG_ON (1 ); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +531,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +547,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +15
-1@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ; --Quoted 2 lines
1.7.6
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
Hi Jaehoon,
While sending patch V2 the wrong version was sent, that doen't include the lock.
This causes the crash that you have seen.
Konstantin is currently at the Linux Embedded Conference and would be able to send the new patch only early next week.
Until then you can use patch V1 to check the performance improvement.
Thanks,
Maya
Quoted 465 lines · Jaehoon Chung
Hi,
I tested with this patch.
But i got some problem. So i want to get your opinion. I used eMMC4.5 card, and using ddr mode, dw-mmc controller. Also use the post/pre-request() in controller.Then i got this message every time.
[ 7.735520] mmc0: new request while areq = eda3046cWhat's wrong?
Best Regards,
Jaehoon ChungQuoted 433 lines · Per Förlin
Hi Konstantin,
Quoted 7 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
Quoted 7 lines
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
Quoted 3 lines
Signed-off-by Konstantin Dorfman <[email protected]>---
Please add a change log here with a brief description of the changes since last version
Quoted 408 lines
drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)drivers/mmc/card/block.c +1
-11@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device");@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq);}
drivers/mmc/card/block.c +5
-2@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + }
mq_rq= container_of(areq, structmmc_queue_req,mmc_active); brq = &mq_rq->brq;drivers/mmc/card/block.c +2
-0@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/*drivers/mmc/card/block.c +1
-1@@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue*mq, struct request *rqc) * prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active, NULL); + mmc_start_req(card->host, &mq_rq->mmc_active,(int *)&status); } } while (ret);drivers/mmc/card/block.c +1
-0@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuingdiscard */
drivers/mmc/card/block.c +1
-1@@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;drivers/mmc/card/queue.c +23
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",host->index, subname ? subname : "");drivers/mmc/card/queue.h +2
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;drivers/mmc/card/queue.h +1
-0@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *,
spinlock_t*,drivers/mmc/core/core.c +18
-0@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card.
+ * Starts data mmc request execution
+ */
+static int__mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)drivers/mmc/core/core.c +11
-0+{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} + static int __mmc_start_req(struct mmc_host *host, struct mmc_request*mrq)
{ init_completion(&mrq->completion);drivers/mmc/core/core.c +7
-0@@ -334,6 +366,57 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request
+ * execution or new request arrives from block layer. Handles
+ * command retries.
+ *
+ * Returns enummmc_blk_statusafter checking errors.
+ */drivers/mmc/core/core.c +23
-0+static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + context_info->is_waiting_last_req = false; +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { + pr_info("%s: req failed (CMD%u): %d,retrying...\n",
drivers/mmc/core/core.c +2
-0+mmc_hostname (host), + cmd->opcode,
cmd->error);drivers/mmc/core/core.c +4
-0+ cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */
drivers/mmc/core/core.c +15
-2+ } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -423,8 +506,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { + pr_err("%s: new request while areq = %p", + mmc_hostname(host),areq);
drivers/mmc/core/core.c +10
-1+BUG_ON (1 ); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +531,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req(host, host->areq->mrq, 0);drivers/mmc/core/core.c +1
-0@@ -452,6 +547,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (error) *error = err; +return data; }
EXPORT_SYMBOL(mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +5
-0@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request
+ * @wait wait queue
+ */include/linux/mmc/core.h +7
-0+struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */
struct mmc_command *cmd;include/linux/mmc/core.h +0
-0@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */
+ structmmc_context_info*context_info;
};Quoted 10 lines
Quoted 4 lines
struct
mmc_host;
--
1.7.6--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
Hi Maya,
Thank you for reply. I will check the patch v1 and share the result.
Best Regards,
Jaehoon Chung
Quoted 479 lines · On 11/08/2012 09:51 PM, [email protected] wrote:
Hi Jaehoon,
While sending patch V2 the wrong version was sent, that doen't include the lock.
This causes the crash that you have seen.
Konstantin is currently at the Linux Embedded Conference and would be able to send the new patch only early next week.
Until then you can use patch V1 to check the performance improvement.Thanks,
MayaQuoted 465 lines · Jaehoon Chung
Hi,
I tested with this patch.
But i got some problem. So i want to get your opinion. I used eMMC4.5 card, and using ddr mode, dw-mmc controller. Also use the post/pre-request() in controller.Then i got this message every time.
[ 7.735520] mmc0: new request while areq = eda3046cWhat's wrong?
Best Regards,
Jaehoon ChungQuoted 433 lines · Per Förlin
Hi Konstantin,
Quoted 7 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
Quoted 7 lines
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
Quoted 3 lines
Signed-off-by Konstantin Dorfman <[email protected]>---
Please add a change log here with a brief description of the changes since last version
Quoted 408 lines
drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)drivers/mmc/card/block.c +1
-11@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device");@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq);}
drivers/mmc/card/block.c +5
-2@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + }
mq_rq= container_of(areq, structmmc_queue_req,mmc_active); brq = &mq_rq->brq;drivers/mmc/card/block.c +2
-0@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/*drivers/mmc/card/block.c +1
-1@@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue*mq, struct request *rqc) * prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active, NULL); + mmc_start_req(card->host, &mq_rq->mmc_active,(int *)&status); } } while (ret);drivers/mmc/card/block.c +1
-0@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuingdiscard */
drivers/mmc/card/block.c +1
-1@@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;drivers/mmc/card/queue.c +23
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",host->index, subname ? subname : "");drivers/mmc/card/queue.h +2
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;drivers/mmc/card/queue.h +1
-0@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *,
spinlock_t*,drivers/mmc/core/core.c +18
-0@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card.
+ * Starts data mmc request execution
+ */
+static int__mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)drivers/mmc/core/core.c +11
-0+{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} + static int __mmc_start_req(struct mmc_host *host, struct mmc_request*mrq)
{ init_completion(&mrq->completion);drivers/mmc/core/core.c +7
-0@@ -334,6 +366,57 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request
+ * execution or new request arrives from block layer. Handles
+ * command retries.
+ *
+ * Returns enummmc_blk_statusafter checking errors.
+ */drivers/mmc/core/core.c +23
-0+static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + context_info->is_waiting_last_req = false; +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { + pr_info("%s: req failed (CMD%u): %d,retrying...\n",
drivers/mmc/core/core.c +2
-0+mmc_hostname (host), + cmd->opcode,
cmd->error);drivers/mmc/core/core.c +4
-0+ cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */
drivers/mmc/core/core.c +15
-2+ } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -423,8 +506,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { + pr_err("%s: new request while areq = %p", + mmc_hostname(host),areq);
drivers/mmc/core/core.c +10
-1+BUG_ON (1 ); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +531,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req(host, host->areq->mrq, 0);drivers/mmc/core/core.c +1
-0@@ -452,6 +547,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (error) *error = err; +return data; }
EXPORT_SYMBOL(mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +5
-0@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request
+ * @wait wait queue
+ */include/linux/mmc/core.h +7
-0+struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */
struct mmc_command *cmd;include/linux/mmc/core.h +0
-0@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */
+ structmmc_context_info*context_info;
};Quoted 10 lines
Quoted 4 lines
struct
mmc_host;
--
1.7.6--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
In reply to Per FörlinQuoted 10 lines · Per Förlin
Hi Konstantin,
Quoted 7 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
Quoted 8 lines
Quoted 7 lines
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
What HW and what test cases have you been running?
I use the msm_sdcc controller and ran lmdd, tiotest, iozone benchmark tool (details were on other mail in this thread).
I ran it on Qualcomm® SnapdragonT S4 ProAPQ8064
Thanks,
Signature · 4 lines
Konstantin Dorfman, QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
In reply to Konstantin DorfmanHi Konstantin,
Quoted 56 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
Signed-off-by Konstantin Dorfman <[email protected]>--- drivers/mmc/card/block.c | 26 +++++------- drivers/mmc/card/queue.c | 26 ++++++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 15 +++++++ 6 files changed, 163 insertions(+), 21 deletions(-)drivers/mmc/card/block.c +1
-11@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1279
Quoted 8 lines
drivers/mmc/card/block.c +3
-2@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST )
I think we can make a line..
Quoted 25 lines
drivers/mmc/card/block.c +5
-1+ mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG_ON (1 );/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/* @@ -1367,7 +1362,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, (int *)&status); } }while (ret);
drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) · line 1404
Quoted 4 lines
drivers/mmc/card/block.c +1
-0@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;
I didn't understand why clearing the MMC_QUEUE_NEW_REQUEST. Sorry, Could you explain to me? I didn't read the previous patch. If mentioned about this before, I will read them.
Best Regards,
Jaehoon Chung
Quoted 299 lines
drivers/mmc/card/block.c +1
-1if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ @@ -1426,7 +1422,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host);return ret;drivers/mmc/card/queue.c +23
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,17 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req) + mq->context_info.is_waiting_last_req = true;spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -111,8 +116,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +278,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +99
-3@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,57 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + context_info->is_waiting_last_req = false; +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -423,8 +506,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG_ON (1 ); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +531,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +547,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +15
-0@@ -120,6 +120,20 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +142,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ;
Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
Hello Jaehoon,
Quoted 2 lines · Jaehoon Chung
Hi Konstantin,
...
Quoted 10 lines
drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) · line 1404
Quoted 6 lines
drivers/mmc/card/block.c +1
-0@@ -1406,6 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret =0 ; goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ;I didn't understand why clearing the
MMC_QUEUE_NEW_REQUEST. Sorry, Could you explain to me? I didn't read the previous patch. If mentioned about this before, I will read them.
Quoted 2 lines
Best Regards,
Jaehoon Chung
This flag used as indication that mmc_blk_issue_rw_rq() returned because of new request notification, so the flow continues rollback to fetch new request from mmc_queue_thread() (queue.c).
Before calling the mmc_blk_issue_rw_rq() need to clear the flag to be able to trigger it again and to differ the case with new packet notification from legacy flow (when returning from mmc_blk_issue_rw_rq() because request is done/finished.
Thanks,
Signature · 4 lines
Konstantin Dorfman, QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v3 1/1 mmc: fix async request mechanism for sequential read scenarios
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.
With this change read throughput is improved by 16%.
---
v3:
- new MMC_QUEUE_NEW_REQUEST flag to mark new request case
- lock added to update is_new_req flag
- condition for sending new req notification changed
- Moved start waiting for new req notification after fetching NULL
v2:
- Removed synchronization flags
- removed lock from done()
- flags names changed
v1:
- Initial submit
drivers/mmc/card/block.c +14 -15
@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );
@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }
@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;
@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG ();/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/*
@@ -1367,7 +1362,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, + (int *)&status); } }while (ret);
@@ -1407,6 +1403,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ if (card->host->areq)
@@ -1426,9 +1424,10 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host); +return ret; }
drivers/mmc/card/queue.c +28 -3
@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff.
@@ -63,11 +62,20 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { + mq->context_info.is_waiting_last_req = true; + }spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request
@@ -103,6 +111,7 @@ static void mmc_request_fn(struct request_queue *q) {struct mmc_queue *mq = q->queuedata;struct request *req; +unsigned long flags;if (!mq) {while ((req =blk_fetch_request (q)) !=NULL ) {
@@ -111,8 +120,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +spin_lock_irqsave (&mq->context_info.lock, flags); +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } +spin_unlock_irqrestore (&mq->context_info.lock, flags); + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }
@@ -262,6 +282,11 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); +spin_lock_init (&mq->context_info.lock); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );
drivers/mmc/card/queue.h +3 -0
@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;
@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
drivers/mmc/core/core.c +110 -17
171 lines not on this page · raw message
include/linux/mmc/card.h +12 -0
@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */
include/linux/mmc/core.h +16 -0
@@ -120,6 +120,21 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +spinlock_t lock; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;
@@ -128,6 +143,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ;
Signature · 5 lines
1.7.6 -- Konstantin Dorfman, QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
RE: [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
Hi Konstantin,
I'm looking into this patch.
Idea looks good as a whole.
Quoted 85 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
Signed-off-by Konstantin Dorfman <[email protected]>---
v3:
- newMMC_QUEUE_NEW_REQUESTflag to mark new request case
- lock added to updateis_new_reqflag
- condition for sending new req notification changed
- Moved start waiting for new req notification after fetching NULL
v2:
- Removed synchronization flags
- removed lock fromdone()
- flags names changed
v1:
- Initial submitdrivers/mmc/card/block.c +8
-13@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG ();/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/*
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1366
Quoted 6 lines
drivers/mmc/card/block.c +2
-1@@ -1367,7 +1362,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, + (int *)&status);
Here, we'll try to send previous request which is not completed successfully.
And then at the top of do~while, mmc_start_req will be called for current request and get the status. Above change looks like unnecessary. Is there any reason?
Thanks,
Seungwon Jeon
Quoted 369 lines
drivers/mmc/card/block.c +4
-1} }while (ret);@@ -1407,6 +1403,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ if (card->host->areq)@@ -1426,9 +1424,10 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host); +return ret; }drivers/mmc/card/queue.c +28
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,20 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { + mq->context_info.is_waiting_last_req = true; + }spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -103,6 +111,7 @@ static void mmc_request_fn(struct request_queue *q) {struct mmc_queue *mq = q->queuedata;struct request *req; +unsigned long flags;if (!mq) {while ((req =blk_fetch_request (q)) !=NULL ) {@@ -111,8 +120,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +spin_lock_irqsave (&mq->context_info.lock, flags); +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } +spin_unlock_irqrestore (&mq->context_info.lock, flags); + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +282,11 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); +spin_lock_init (&mq->context_info.lock); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +110
-17@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,60 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; +unsigned long flags; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); +spin_lock_irqsave (&context_info->lock, flags); + context_info->is_waiting_last_req = false; +spin_unlock_irqrestore (&context_info->lock, flags); +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -396,23 +482,17 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, }/** -* mmc_start_req - start a non-blocking request -* @host: MMC host to start command -* @areq: async request to start -* @error: out parameter returns 0 for success, otherwise non zero -* -* Start a new MMC custom command request for a host. -* If there is on ongoing async request wait for completion -* of that request and start the new one and return. -* Does not wait for the new request to complete. +* mmc_start_req - start a non-blocking data request +* @host: MMC host to start the command +* @areq: async request to start +* @error: out parameter; returns 0 for success, otherwise non zero * -* Returns the completed request, NULL in case of none completed. -* Wait for the an ongoing request (previoulsy started) to complete and -* return the completed request. If there is no ongoing request, NULL -* is returned without waiting. NULL is not an error condition. +* Wait for the ongoing request (previoulsy started) to complete and +* return the completed request. If there is no ongoing request, NULL +* is returned without waiting. NULL is not an error condition. */ struct mmc_async_req *mmc_start_req (struct mmc_host *host, -struct mmc_async_req *areq,int *error) +struct mmc_async_req *areq,int *error) {int err =0 ;int start_err =0 ;@@ -423,8 +503,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG (); + } + *error = err; +/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +528,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +544,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +16
-1@@ -120,6 +120,21 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +spinlock_t lock; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +143,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ; --1.7.6
--
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
I checked this commit.
v3 doesn't work in dw_mmc host driver.
It makes some driver warning while v1 is fine.
I'm finding the reason. And could you check minor one below?
On Tuesday, November 13, 2012, Seungwon Jeon <[email protected]>
Quoted 368 lines
Hi Konstantin,
I'm looking into this patch.
Idea looks good as a whole.Quoted 85 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
Signed-off-by Konstantin Dorfman <[email protected]>---
v3:
- newMMC_QUEUE_NEW_REQUESTflag to mark new request case
- lock added to updateis_new_reqflag
- condition for sending new req notification changed
- Moved start waiting for new req notification after fetching NULL
v2:
- Removed synchronization flags
- removed lock fromdone()
- flags names changed
v1:
- Initial submitdrivers/mmc/card/block.c +8
-13@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG ();/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/* drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1366
Quoted 6 lines
drivers/mmc/card/block.c +2
-1@@ -1367,7 +1362,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, + (int *)&status);Here, we'll try to send previous request which is not completed successfully.
And then at the top of do~while,mmc_start_reqwill be called for current request and get the status. Above change looks like unnecessary. Is there any reason?Thanks,
Seungwon JeonQuoted 264 lines
drivers/mmc/card/block.c +4
-1} }while (ret);@@ -1407,6 +1403,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ if (card->host->areq)@@ -1426,9 +1424,10 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host); +return ret; }drivers/mmc/card/queue.c +28
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,20 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { + mq->context_info.is_waiting_last_req = true; + }spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -103,6 +111,7 @@ static void mmc_request_fn(struct request_queue *q) {struct mmc_queue *mq = q->queuedata;struct request *req; +unsigned long flags;if (!mq) {while ((req =blk_fetch_request (q)) !=NULL ) {@@ -111,8 +120,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +spin_lock_irqsave (&mq->context_info.lock, flags); +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } +spin_unlock_irqrestore (&mq->context_info.lock, flags); + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +282,11 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); +spin_lock_init (&mq->context_info.lock); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +94
-14@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,60 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; +unsigned long flags; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); +spin_lock_irqsave (&context_info->lock, flags); + context_info->is_waiting_last_req = false; +spin_unlock_irqrestore (&context_info->lock, flags); +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -396,23 +482,17 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, }/** -* mmc_start_req - start a non-blocking request -* @host: MMC host to start command -* @areq: async request to start -* @error: out parameter returns 0 for success, otherwise non zero -* -* Start a new MMC custom command request for a host. -* If there is on ongoing async request wait for completion -* of that request and start the new one and return. -* Does not wait for the new request to complete. +* mmc_start_req - start a non-blocking data request +* @host: MMC host to start the command +* @areq: async request to start +* @error: out parameter; returns 0 for success, otherwise non zero * -* Returns the completed request, NULL in case of none completed. -* Wait for the an ongoing request (previoulsy started) to complete and -* return the completed request. If there is no ongoing request, NULL -* is returned without waiting. NULL is not an error condition. +* Wait for the ongoing request (previoulsy started) to complete and +* return the completed request. If there is no ongoing request, NULL +* is returned without waiting. NULL is not an error condition. */ struct mmc_async_req *mmc_start_req (struct mmc_host *host, -struct mmc_async_req *areq,int *error) +struct mmc_async_req *areq,int *error) {int err =0 ;int start_err =0 ;@@ -423,8 +503,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);
Quoted 11 lines
drivers/mmc/core/core.c struct mmc_async_req *mmc_start_req(struct mmc_host *host, · line 513
Quoted 11 lines
drivers/mmc/core/core.c +8
-2if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG (); + } + *error = err;
Need to check NULL of error.
if (error)
Thanks,
Seungwon Jeon
Quoted 99 lines
Quoted 90 lines
drivers/mmc/core/core.c +8
-1+/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +528,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +544,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +16
-1@@ -120,6 +120,21 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +spinlock_t lock; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +143,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ; --1.7.6
--
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux FoundationQuoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
On Thursday, November 15, 2012, Seungwon Jeon <[email protected]>
Quoted 4 lines
I checked this commit.
v3 doesn't work indw_mmchost driver.
It makes some driver warning while v1 is fine.
I'm finding the reason. And could you check minor one below?
Please let me correct my test. There is something applied wrong. Anyway I saw the working fine. Sorry for the confusion.
Thanks,
Seungwon Jeon
Quoted 490 lines
On Tuesday, November 13, 2012, Seungwon Jeon <[email protected]>
Quoted 368 lines
Hi Konstantin,
I'm looking into this patch.
Idea looks good as a whole.Quoted 85 lines · Konstantin Dorfman
When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in latency to start new request.
This change allows to wake up the MMC thread (which
is waiting for the current running request to complete). Now once the MMC thread is woken up, new request can be fetched and prepared in parallel to current running request which means this new request can be started immediately after the current running request completes.With this change read throughput is improved by 16%.
Signed-off-by Konstantin Dorfman <[email protected]>---
v3:
- newMMC_QUEUE_NEW_REQUESTflag to mark new request case
- lock added to updateis_new_reqflag
- condition for sending new req notification changed
- Moved start waiting for new req notification after fetching NULL
v2:
- Removed synchronization flags
- removed lock fromdone()
- flags names changed
v1:
- Initial submitdrivers/mmc/card/block.c +8
-13@@ -112,17 +112,6 @@ struct mmc_blk_data {static DEFINE_MUTEX (open_lock); -enum mmc_blk_status { -MMC_BLK_SUCCESS =0 , -MMC_BLK_PARTIAL , -MMC_BLK_CMD_ERR , -MMC_BLK_RETRY , -MMC_BLK_ABORT , -MMC_BLK_DATA_ERR , -MMC_BLK_ECC_ERR , -MMC_BLK_NOMEDIUM , -}; -module_param (perdev_minors, int,0444 );MODULE_PARM_DESC (perdev_minors,"Minors numbers to allocate per device" );@@ -1225,6 +1214,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mqrq->mmc_active.mrq = &brq->mrq; mqrq->mmc_active.err_check = mmc_blk_err_check; + mqrq->mmc_active.mrq->context_info = &mq->context_info;mmc_queue_bounce_pre (mqrq); }@@ -1284,9 +1274,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = &mq->mqrq_cur->mmc_active; }else areq =NULL ; - areq =mmc_start_req (card->host, areq, (int *) &status); -if (!areq) + areq =mmc_start_req (card->host, areq, (int *)&status); +if (!areq) { +if (status ==MMC_BLK_NEW_REQUEST ) + mq->flags |=MMC_QUEUE_NEW_REQUEST ;return 0 ; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq;@@ -1295,6 +1288,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)mmc_queue_bounce_post (mq_rq);switch (status) { +case MMC_BLK_NEW_REQUEST : +BUG ();/* should never get here */ case MMC_BLK_SUCCESS :case MMC_BLK_PARTIAL :/* drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1366
Quoted 6 lines
drivers/mmc/card/block.c +2
-1@@ -1367,7 +1362,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)* prepare it again and resend. */ mmc_blk_rw_rq_prep (mq_rq, card, disable_multi, mq); -mmc_start_req (card->host, &mq_rq->mmc_active,NULL ); +mmc_start_req (card->host, &mq_rq->mmc_active, + (int *)&status);Here, we'll try to send previous request which is not completed successfully.
And then at the top of do~while,mmc_start_reqwill be called for current request and get the status. Above change looks like unnecessary. Is there any reason?Thanks,
Seungwon JeonQuoted 264 lines
drivers/mmc/card/block.c +4
-1} }while (ret);@@ -1407,6 +1403,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +if (req && req->cmd_flags &REQ_DISCARD ) {/* complete ongoing async transfer before issuing discard */ if (card->host->areq)@@ -1426,9 +1424,10 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } out: -if (!req) +if (!req && !(mq->flags &MMC_QUEUE_NEW_REQUEST ))/* release host only when there are no more requests */ mmc_release_host (card->host); +return ret; }drivers/mmc/card/queue.c +28
-3@@ -22,7 +22,6 @@#define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0)/* * Prepare a MMC request. This just filters out odd stuff. @@ -63,11 +62,20 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_INTERRUPTIBLE ); req =blk_fetch_request (q); mq->mqrq_cur->req = req; +if (!req && mq->mqrq_prev->req && + !(mq->mqrq_prev->req->cmd_flags &REQ_FLUSH ) && + !(mq->mqrq_prev->req->cmd_flags &REQ_DISCARD )) { + mq->context_info.is_waiting_last_req = true; + }spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) {set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); +if (mq->flags &MMC_QUEUE_NEW_REQUEST ) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST ; +continue ;/* fetch again */ + }/* * Current request becomes previous request @@ -103,6 +111,7 @@ static void mmc_request_fn(struct request_queue *q) {struct mmc_queue *mq = q->queuedata;struct request *req; +unsigned long flags;if (!mq) {while ((req =blk_fetch_request (q)) !=NULL ) {@@ -111,8 +120,19 @@ static void mmc_request_fn(struct request_queue *q) }return ; } - -if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) +if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { +/* +* New MMC request arrived when MMC thread may be +* blocked on the previous request to be complete +* with no current request fetched +*/ +spin_lock_irqsave (&mq->context_info.lock, flags); +if (mq->context_info.is_waiting_last_req) { + mq->context_info.is_new_req = true; +wake_up_interruptible (&mq->context_info.wait); + } +spin_unlock_irqrestore (&mq->context_info.lock, flags); + }else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)wake_up_process (mq->thread); }@@ -262,6 +282,11 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, }sema_init (&mq->thread_sem,1 ); +spin_lock_init (&mq->context_info.lock); + mq->context_info.is_new_req = false; + mq->context_info.is_done_rcv = false; + mq->context_info.is_waiting_last_req = false; +init_waitqueue_head (&mq->context_info.wait); mq->thread =kthread_run (mmc_queue_thread, mq,"mmcqd/%d%s" , host->index, subname ? subname :"" );drivers/mmc/card/queue.h +3
-0@@ -26,6 +26,8 @@ struct mmc_queue {struct mmc_card *card;struct task_struct *thread;struct semaphore thread_sem; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1)unsigned int flags;int (*issue_fn)(struct mmc_queue *,struct request *);void *data;@@ -33,6 +35,7 @@ struct mmc_queue {struct mmc_queue_req mqrq[2 ];struct mmc_queue_req *mqrq_cur;struct mmc_queue_req *mqrq_prev; +struct mmc_context_info context_info; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,drivers/mmc/core/core.c +94
-14@@ -316,11 +316,43 @@ out: }EXPORT_SYMBOL (mmc_start_bkops); +/* +* mmc_wait_data_done() - done callback for data request +* @mrq: done data request +* +* Wakes up mmc context, passed as callback to host controller driver +*/ +static void mmc_wait_data_done (struct mmc_request *mrq) +{ + mrq->context_info->is_done_rcv = true; +wake_up_interruptible (&mrq->context_info->wait); +} +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion); } +/* +*__mmc_start_data_req() - starts data request +* @host: MMC host to start the request +* @mrq: data request to start +* +* Fills done callback that will be used when request are done by card. +* Starts data mmc request execution +*/ +static int __mmc_start_data_req (struct mmc_host *host,struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; +if (mmc_card_removed (host->card)) { + mrq->cmd->error = -ENOMEDIUM ; +return -ENOMEDIUM ; + } +mmc_start_request (host, mrq); + +return 0 ; +} +static int __mmc_start_req (struct mmc_host *host,struct mmc_request *mrq) {init_completion (&mrq->completion);@@ -334,6 +366,60 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)return 0 ; } +/* +* mmc_wait_for_data_req_done() - wait for request completed or new +* request notification arrives +* @host: MMC host to prepare the command. +* @mrq: MMC request to wait for +* +* Blocks MMC context till host controller will ack end of data request +* execution or new request arrives from block layer. Handles +* command retries. +* +* Returns enum mmc_blk_status after checking errors. +*/ +static int mmc_wait_for_data_req_done (struct mmc_host *host, +struct mmc_request *mrq) +{ +struct mmc_command *cmd; +struct mmc_context_info *context_info = mrq->context_info; +int err; +unsigned long flags; + +while (1 ) { +wait_event_interruptible (context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); +spin_lock_irqsave (&context_info->lock, flags); + context_info->is_waiting_last_req = false; +spin_unlock_irqrestore (&context_info->lock, flags); +if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; +if (!cmd->error || !cmd->retries || +mmc_card_removed (host->card)) { + err = host->areq->err_check (host->card, + host->areq); +break ;/* return err */ + }else { +pr_info ("%s: req failed (CMD%u): %d, retrying...\n" , +mmc_hostname (host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error =0 ; + host->ops->request (host, mrq); +continue ;/* wait for done/new event again */ + } + }else if (context_info->is_new_req) { + context_info->is_new_req = false; + err =MMC_BLK_NEW_REQUEST ; +break ;/* return err */ + } + }/* while */ +return err; +} +static void mmc_wait_for_req_done (struct mmc_host *host,struct mmc_request *mrq) {@@ -396,23 +482,17 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, }/** -* mmc_start_req - start a non-blocking request -* @host: MMC host to start command -* @areq: async request to start -* @error: out parameter returns 0 for success, otherwise non zero -* -* Start a new MMC custom command request for a host. -* If there is on ongoing async request wait for completion -* of that request and start the new one and return. -* Does not wait for the new request to complete. +* mmc_start_req - start a non-blocking data request +* @host: MMC host to start the command +* @areq: async request to start +* @error: out parameter; returns 0 for success, otherwise non zero * -* Returns the completed request, NULL in case of none completed. -* Wait for the an ongoing request (previoulsy started) to complete and -* return the completed request. If there is no ongoing request, NULL -* is returned without waiting. NULL is not an error condition. +* Wait for the ongoing request (previoulsy started) to complete and +* return the completed request. If there is no ongoing request, NULL +* is returned without waiting. NULL is not an error condition. */ struct mmc_async_req *mmc_start_req (struct mmc_host *host, -struct mmc_async_req *areq,int *error) +struct mmc_async_req *areq,int *error) {int err =0 ;int start_err =0 ;@@ -423,8 +503,20 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,mmc_pre_req (host, areq->mrq, !host->areq);Quoted 11 lines
drivers/mmc/core/core.c struct mmc_async_req *mmc_start_req(struct mmc_host *host, · line 513
Quoted 11 lines
drivers/mmc/core/core.c +8
-2if (host->areq) { -mmc_wait_for_req_done (host, host->areq->mrq); - err = host->areq->err_check (host->card, host->areq); + err =mmc_wait_for_data_req_done (host, host->areq->mrq); +if (err ==MMC_BLK_NEW_REQUEST ) { +if (areq) { +pr_err ("%s: new request while areq = %p" , +mmc_hostname (host), areq); +BUG (); + } + *error = err;Need to check NULL of error.
if (error)Thanks,
Seungwon JeonQuoted 90 lines
Quoted 90 lines
drivers/mmc/core/core.c +8
-1+/* +* The previous request was not completed, +* nothing to return +*/ +return NULL ; + }/* * Check BKOPS urgency for each R1 response */ @@ -436,7 +528,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, }if (!err && areq) - start_err =__mmc_start_req (host, areq->mrq); + start_err =__mmc_start_data_req (host, areq->mrq);if (host->areq)mmc_post_req (host, host->areq->mrq,0 );@@ -452,6 +544,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,if (error) *error = err; +return data; }EXPORT_SYMBOL (mmc_start_req);include/linux/mmc/card.h +12
-0@@ -186,6 +186,18 @@ struct sdio_func_tuple;#define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { +MMC_BLK_SUCCESS =0 , +MMC_BLK_PARTIAL , +MMC_BLK_CMD_ERR , +MMC_BLK_RETRY , +MMC_BLK_ABORT , +MMC_BLK_DATA_ERR , +MMC_BLK_ECC_ERR , +MMC_BLK_NOMEDIUM , +MMC_BLK_NEW_REQUEST , +}; +/* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ include/linux/mmc/core.h +16
-1@@ -120,6 +120,21 @@ struct mmc_data {s32 host_cookie;/* host private data */ }; +/** +* mmc_context_info - synchronization details for mmc context +* @is_done_rcv wake up reason was done request +* @is_new_req wake up reason was new request +* @is_waiting_last_req mmc context waiting for single running request +* @wait wait queue +*/ +struct mmc_context_info { +bool is_done_rcv; +bool is_new_req; +bool is_waiting_last_req; +wait_queue_head_t wait; +spinlock_t lock; +}; +struct mmc_request {struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd;@@ -128,6 +143,7 @@ struct mmc_request {struct completion completion;void (*done)(struct mmc_request *);/* completion function */ +struct mmc_context_info *context_info; };struct mmc_host ; --1.7.6
--
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux FoundationQuoted 9 lines
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v3 1/1 mmc: core: Add support for idle time BKOPS
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updating BKOPS_STATUS (EXT_CSD byte [246]).
According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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.
Changes in v3:
- Move the call to stop_bkops to block.c.
This allows us to remove the mmc_claim_host from inside the function and doesn't cause additional degradation due to un-neccessary calim host operation
Changes in v2:
- Check the number of written / discarded sectors as the trigger for checking the BKOPS need.
- Code review fixes
--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)
drivers/mmc/card/block.c +7 -1
@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card); + } + } ret =mmc_blk_part_switch (card, md);if (ret) {
drivers/mmc/card/queue.c +2 -0
@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;
@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);
drivers/mmc/core/core.c +167 -11
241 lines not on this page · raw message
drivers/mmc/core/mmc.c +23 -0
@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + } + } +if (!oldcard) host->card = card;
include/linux/mmc/card.h +35 -0
@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */ +#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed; +}; +/* * MMC device */
@@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/*
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 );
Signature · 4 lines
1.7.3.3 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Chris,
I managed to find a solution in which there is no need to check the number of written / discarded sectors as a trigger for BKOPS status check. I moved the code that checks the need to stop the BKOPS to mmc/block code, in which there is no need for additional claim_host and remove_host operations.
Please review this patch.
Thanks,
Maya
Quoted 478 lines · Maya Erez
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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.Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the function
and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +7
-1@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card); + } + } ret =mmc_blk_part_switch (card, md);drivers/mmc/card/block.c +0
-0if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +28
-1@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested.
drivers/mmc/core/core.c +28
-6@@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ;drivers/mmc/core/core.c +96
-1use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {drivers/mmc/core/core.c +15
-3complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already +* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +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));return err; } -mmc_card_clr_doing_bkops (host->card); }drivers/mmc/core/core.c +0
-0spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +23
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +26
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
include/linux/mmc/card.h +9
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed; +}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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);--
1.7.3.3
--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Maya,
Quoted 6 lines · On Sun, Nov 25 2012, [email protected] wrote:
I managed to find a solution in which there is no need to check the number of written / discarded sectors as a trigger for BKOPS status check. I moved the code that checks the need to stop the BKOPS to mmc/block code, in which there is no need for additional
claim_hostandremove_hostoperations.
Please review this patch.
Looks good -- if we find that counting with a bitmask of eraseblocks is necessary it can be added on later.
Do you have a testing plan for the patch? I wonder if we could test the effects on battery life and latency.
Thanks!
- Chris.
Signature · 2 lines
Chris Ball <[email protected]> <http://printf.net/> One Laptop Per Child
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
In reply to Maya ErezHi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.) Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance is decreased. Well, as Chris is mentioned, do you have any test plan?
So I will test more with this patch, because i want to test with dw-mmc controller, too.
Quoted 64 lines · Maya Erez
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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.Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the function and doesn't cause additional degradation due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger for checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +0
-0@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;
drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) · line 1403
Quoted 10 lines
drivers/mmc/card/block.c +5
-1struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card);
We didn't need to check whether mmc_stop_bkops is success or not? If mmc_stop_bkops() is failed, then bkops is continuously running.
Best Regards,
Jaehoon Chung
Quoted 378 lines
drivers/mmc/card/block.c +2
-0+ } + } ret =mmc_blk_part_switch (card, md);if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +167
-11@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)* bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already +* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block,switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +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));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +23
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +35
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */ +#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed; +}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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 );
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded.
I verified it with the msm_sdcc controller.
Chris - We do expect it to influence the battery consumption, since we now delay getting into suspend (since mmc_start_bkops which is called after the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten the timeout given to pm_schedule_suspend by the periodic BKOPS idle time. Does it make sense to you?
Thanks,
Maya
Quoted 493 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.)
Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance is decreased.
Well, as Chris is mentioned, do you have any test plan?
So I will test more with this patch, because i want to test with dw-mmc controller, too.Quoted 74 lines · Maya Erez
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS
(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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 are 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.Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the
function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +0
-0@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue *mq, · line 1404
Quoted 10 lines
drivers/mmc/card/block.c +5
-1struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 387 lines
drivers/mmc/card/block.c +2
-0+ } + } ret =mmc_blk_part_switch (card, md);drivers/mmc/card/block.c +0
-0if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +28
-1@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested.
drivers/mmc/core/core.c +28
-6@@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ;drivers/mmc/core/core.c +96
-1use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops); +/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS. +* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {drivers/mmc/core/core.c +15
-3complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already +* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; } +out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +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));return err; } -mmc_card_clr_doing_bkops (host->card); }drivers/mmc/core/core.c +0
-0spin_lock_irqsave (&host->lock, flags);drivers/mmc/core/mmc.c +23
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } +if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling); + +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +26
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms; +unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
include/linux/mmc/card.h +9
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed; +}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* 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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Quoted 15 lines · On 3 December 2012 10:49, <[email protected]> wrote:
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded.
I verified it with themsm_sdcccontroller.Chris - We do expect it to influence the battery consumption, since we now delay getting into suspend (since
mmc_start_bkopswhich is called after the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten the timeout given topm_schedule_suspendby the periodic BKOPS idle time. Does it make sense to you?
Quoted 40 lines
Thanks,
MayaQuoted 37 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.)
Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance is decreased.
Well, as Chris is mentioned, do you have any test plan?
So I will test more with this patch, because i want to test with dw-mmc controller, too.Quoted 23 lines · Maya Erez
Devices have various maintenance operations need to perform internally. In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS
(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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.
What host controller suspend timeout are you referring to here?
If you are thinking of the runtime PM autosuspend timeout used in many host driver, then you might have missunderstand how runtime PM is used in host drivers.
This has nothing to do with BKOPS as such, unless you think that the card must be kept clocked during BKOPS operations, but then this needs to be stated somewhere in this patch and that is not the case.
Moreover, I could not find any new timeout added for the _host_ struct in this patch.
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
If BKOPS are 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.
Not sure of what suspend you are talking about here. But for sure BKOPS must _never_ prevent a system suspend.
You might want to prevent a host from being runtime suspended though, but that is not accomplished in this patch.
Quoted 206 lines
Quoted 206 lines
Quoted 48 lines
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.
Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the
function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +0
-0@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue *mq, · line 1404
Quoted 10 lines
drivers/mmc/card/block.c +5
-1struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 142 lines
drivers/mmc/card/block.c +2
-0+ } + } ret =mmc_blk_part_switch (card, md);if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +28
-1@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between +* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested.
drivers/mmc/core/core.c +28
-6@@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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 (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ;use_busy_signal = true;drivers/mmc/core/core.c +7
-1@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card, boolfrom_exception) * bkops executed synchronously, otherwise * the operation is in progress */ -if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops);
Quoted 8 lines
Quoted 8 lines
drivers/mmc/core/core.c void mmc_start_bkops(struct mmc_card *card, bool from_exception) · line 381
Quoted 8 lines
drivers/mmc/core/core.c +8
-0+/** +* mmc_bkops_completion_polling() - Poll on the card status to +* wait for the non-blocking BKOPS completion +* @work: The completion polling work +* +* The on-going reading of the card status will prevent the card +* from getting into suspend while it is in the middle of +* performing BKOPS.
Not true! Suspend will not be prevented by doing a mmc_send_status. Moreover, I would be interested to understand more about why you need to prevent "suspend". Please elaborate why you think this is needed.
Quoted 106 lines
Quoted 106 lines
Quoted 106 lines
drivers/mmc/core/core.c +81
-0+* Since the non blocking BKOPS can be interrupted by a fetched +* request we also check IF mmc_card_doing_bkops in each +* iteration. +*/ +void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent +* the host from getting into suspend +*/ +do { +mmc_claim_host (card->host); + +if (!mmc_card_doing_bkops (card)) + goto out; + + err =mmc_send_status (card, &status); +if (err) { +pr_err ("%s: error %d requesting status\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* +* Some cards mishandle the status bits, so make sure to check +* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { +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; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the +* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed +* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete(&mrq->completion);drivers/mmc/core/core.c +11
-0@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already +* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; }
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/core.c +1
-0+out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {Quoted 8 lines
drivers/mmc/core/core.c int mmc_pm_notify(struct notifier_block *notify_block, · line 2668
Quoted 8 lines
drivers/mmc/core/core.c +3
-2case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { +mmc_claim_host (host); err =mmc_stop_bkops (host->card); +mmc_release_host (host);
This code seems a bit strange. You will check for mmc_card_mmc, but not all (e)MMC will support bkops. How about acually just checking if bkops is "on" or "off" somehow.
Additionally, so this piece of code shall stop an ongoing bkops before going to suspend, so that make sense. But would it not be more meaningfull to take care of that in mmc_suspend_host? I mean why do yo need to do it in the PM_SUSPEND_PREPARE notification sequence especially?
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
drivers/mmc/core/core.c +0
-1if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave(&host->lock, flags);drivers/mmc/core/mmc.c +0
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } }
Quoted 6 lines
Quoted 6 lines
drivers/mmc/core/mmc.c static int mmc_init_card(struct mmc_host *host, u32 ocr, · line 1266
Quoted 6 lines
drivers/mmc/core/mmc.c +6
-0+if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling);
I guess you don't have "removable" eMMC with BKOPS support so this code will in practice only be executed once for an eMMC, so we are safe. But still I am not fond of putting this code for workqueue here. Either that should be done as a part of when the card is created/deleted or when then host is created/deleted.
Quoted 38 lines
Quoted 38 lines
Quoted 38 lines
drivers/mmc/core/mmc.c +17
-0+ +/* +* 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. +*/ + card->bkops_info.delay_ms =MMC_IDLE_BKOPS_TIME_MS ; +if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms =min ( + card->bkops_info.delay_ms, + card->bkops_info.host_suspend_tout_ms/2 ); + } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +10
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @poll_for_completion: Poll on BKOPS completion +* @cancel_delayed_work: A flag to indicate if the delayed work +* should be cancelled
Quoted 8 lines
Quoted 8 lines
include/linux/mmc/card.h struct mmc_part { · line 228
Quoted 8 lines
include/linux/mmc/card.h +8
-0+* @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 +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms;
As stated earlier, what is this timeout you are referring to? What suspend?
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
include/linux/mmc/card.h +6
-0+unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd +* is idle. +*/ +#define MMC_IDLE_BKOPS_TIME_MS 2000
Quoted 8 lines
Quoted 8 lines
include/linux/mmc/card.h struct mmc_part { · line 241
Quoted 8 lines
include/linux/mmc/card.h +2
-0+struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
include/linux/mmc/card.h +5
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed;
Could not find "sectors_changed" being used. Maybe you forgot to remove this from previous version of the patch.
Quoted 40 lines
Quoted 24 lines
Quoted 24 lines
include/linux/mmc/card.h +4
-0+}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */ unsigned int nr_parts; + +struct mmc_bkops_info bkops_info; };/* include/linux/mmc/core.h +0
-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);Quoted 10 lines
Quoted 8 lines
include/linux/mmc/core.h +3
-0+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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Quoted 5 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Finally some overall thoughts. What I would like to understand is how we decide that the card has become "idle". I belive two values should be considered, but are they?
1. The card need BKOPS to be performed for some status level.
2. Request inactivity for a certain timeout has occured.
Have you considered to use runtime PM for the card device instead of the workqueue?
Kind regards
Ulf Hansson
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Ulf,
Let me try to better explain:
The idea behind the periodic BKOPS is to check the card's need for BKOPS periodically in order to prevent an urgent BKOPS need by the card. In order to actually manage to prevent the urgent BKOPS need, the host should give the card enough time to perform the BKOPS (when it recognizes BKOPS need), otherwise there is no point in having the periodic BKOPS. The above results in the following:
1. After starting non-urgent BKOPS we "delay" getting into suspend by polling on the card's status (explanation below), in order to give the card time to perform the BKOPS. This has no effect on the power consumption since the same BKOPS operations that were performed after the foregound operation just moved to another location, meaning before going into suspend.
2. Using PM_SUSPEND_PREPARE instead of the workqueue would not be efficient since we don't want to wait until the host is ready to get into suspend and then prevent him from suspending by doing BKOPS operations that can take a long time. It is better to start the BKOPS earlier.
I am not too familiar with the controllers code and also my understanding in runtime suspend is very basic, so feel free to correct me if I'm wrong here or the behavior in other controllers is different from msm_sdcc. mmc_claim_host calls host->ops->enable. This API is implemented per controller but as far as I understand it, this API must prevent suspend, otherwise we might go into suspend while there is bus activity. By doing get_card_status we call mmc_claim_host and this call is the one that "delays" getting into suspend.
If this is not the case in other controllers than the BKOPS will not prevent the suspend and BKOPS will be interrupted.
As for the effect on the battery consumption, this is probably something specific to our controller, so sorry if I created a confusion.
Additional comments inline.
Thanks,
Maya
Quoted 71 lines · Ulf Hansson
Quoted 17 lines · On 3 December 2012 10:49, <[email protected]> wrote:
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded.
I verified it with themsm_sdcccontroller.Chris - We do expect it to influence the battery consumption, since we now
delay getting into suspend (sincemmc_start_bkopswhich is called after the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten the timeout given topm_schedule_suspendby the periodic BKOPS idle time.
Does it make sense to you?Quoted 43 lines
Thanks,
MayaQuoted 40 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.)
Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance
is decreased.
Well, as Chris is mentioned, do you have any test plan?
So I will test more with this patch, because i want to test with dw-mmc controller, too.Quoted 25 lines · Maya Erez
Devices have various maintenance operations need to perform internally.
In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called
Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS
(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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.
What host controller suspend timeout are you referring to here?
If you are thinking of the runtime PM autosuspend timeout used in many host driver, then you might have missunderstand how runtime PM is used in host drivers.
This has nothing to do with BKOPS as such, unless you think that the card must be kept clocked during BKOPS operations, but then this needs to be stated somewhere in this patch and that is not the case.
Quoted 2 lines
Moreover, I could not find any new timeout added for the _host_ struct in this patch.
Yes, I was referring to the runtime PM autosuspend timeout. Since we want to give the BKOPS time to be performed before going into suspend, we need to take this timeout into account.
Quoted 11 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If BKOPS are 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.Not sure of what suspend you are talking about here. But for sure BKOPS must _never_ prevent a system suspend.
You might want to prevent a host from being runtime suspended though, but that is not accomplished in this patch.
This is explained in my general comment. Let me know if it is still not clear.
Quoted 216 lines
Quoted 216 lines
Quoted 216 lines
Quoted 54 lines
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.
Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_host
was moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the
function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger
for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +0
-0@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card;drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue · line 1406
Quoted 10 lines
drivers/mmc/card/block.c +5
-1-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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 146 lines
drivers/mmc/card/block.c +2
-0+ } + }ret = mmc_blk_part_switch(card, md); if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +15
-0@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between
drivers/mmc/core/core.c +37
-5+* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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
drivers/mmc/core/core.c +4
-2+* for urgent BKOPS, BKOPs will be performed by the delayed BKOPs +* work, before going to suspend +*/ if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)
* bkops executed synchronously, otherwise
* the operation is in progress
*/drivers/mmc/core/core.c +7
-1-if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops);
Quoted 12 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
+/**
+ *mmc_bkops_completion_polling()- Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.Not true! Suspend will not be prevented by doing a
mmc_send_status. Moreover, I would be interested to understand more about why you need to prevent "suspend". Please elaborate why you think this is needed.
This is explained in my general comment. Let me know if it is still not clear.
Quoted 133 lines
Quoted 113 lines
Quoted 113 lines
Quoted 113 lines
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IFmmc_card_doing_bkopsin each
+ * iteration.
+ */drivers/mmc/core/core.c +11
-0+void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent
drivers/mmc/core/core.c +17
-0+* the host from getting into suspend +*/ + do { + mmc_claim_host(card->host); + + if (!mmc_card_doing_bkops(card)) + goto out; + + err = mmc_send_status(card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(card->host), err); + goto out; + } + + /* + * Some cards mishandle the status bits, so make sure tocheck
drivers/mmc/core/core.c +5
-0+* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { + pr_debug("%s: %s: completed BKOPs, exitpolling\n",
drivers/mmc/core/core.c +10
-0+ mmc_hostname(card->host), __func__); +mmc_card_clr_doing_bkops (card); + card->bkops_info.started_delayed_bkops = false; + goto out; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the
drivers/mmc/core/core.c +25
-0+* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed
drivers/mmc/core/core.c +12
-0+* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already
drivers/mmc/core/core.c +8
-0+* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; }Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/core.c +1
-0+out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {Quoted 8 lines
drivers/mmc/core/core.c int mmc_pm_notify(struct notifier_block *notify_block, · line 2668
Quoted 8 lines
drivers/mmc/core/core.c +3
-2case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { +mmc_claim_host (host); err =mmc_stop_bkops (host->card); +mmc_release_host (host);This code seems a bit strange. You will check for
mmc_card_mmc, but not all (e)MMC will support bkops. How about acually just checking if bkops is "on" or "off" somehow.
Quoted 5 lines
Additionally, so this piece of code shall stop an ongoing bkops before going to suspend, so that make sense. But would it not be more meaningfull to take care of that in
mmc_suspend_host? I mean why do yo need to do it in thePM_SUSPEND_PREPAREnotification sequence especially?
This code was not added by me. I just added the mmc_claim_host(host)/mmc_release_host calls. Maybe Jaehoon, who added this code in the BKOPS patch can respond to your comment.
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
drivers/mmc/core/core.c +0
-1if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave(&host->lock, flags);drivers/mmc/core/mmc.c +0
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } }
Quoted 12 lines
Quoted 6 lines
Quoted 6 lines
drivers/mmc/core/mmc.c static int mmc_init_card(struct mmc_host *host, u32 ocr, · line 1266
Quoted 6 lines
drivers/mmc/core/mmc.c +6
-0+if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling);I guess you don't have "removable" eMMC with BKOPS support so this code will in practice only be executed once for an eMMC, so we are safe. But still I am not fond of putting this code for workqueue here. Either that should be done as a part of when the card is created/deleted or when then host is created/deleted.
I will check if there could be a better place for this.
Quoted 48 lines
Quoted 48 lines
Quoted 48 lines
Quoted 48 lines
drivers/mmc/core/mmc.c +3
-0+ +/* +* 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
conditiondrivers/mmc/core/mmc.c +2
-0+* before starting BKOPs and going into suspend. +* If the host controller didn't set its idle time,
drivers/mmc/core/mmc.c +3
-0+* a default value is used. +*/ + card->bkops_info.delay_ms =
MMC_IDLE_BKOPS_TIME_MS;drivers/mmc/core/mmc.c +4
-0+if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms = min( + card->bkops_info.delay_ms, +card->bkops_info.host_suspend_tout_ms/2);
drivers/mmc/core/mmc.c +3
-0+ } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +13
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 228
Quoted 5 lines
include/linux/mmc/card.h +5
-0+* discard since the last idle BKOPS were scheduled +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms;As stated earlier, what is this timeout you are referring to? What suspend?
Explained above.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
include/linux/mmc/card.h +3
-0+unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */include/linux/mmc/card.h +3
-0+#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 241
Quoted 5 lines
include/linux/mmc/card.h +5
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed;Could not find "
sectors_changed" being used. Maybe you forgot to remove this from previous version of the patch.
Yes, this should be removed.
Quoted 66 lines
Quoted 43 lines
Quoted 26 lines
Quoted 26 lines
include/linux/mmc/card.h +2
-0+}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */
unsigned int nr_parts;include/linux/mmc/card.h +2
-0+ +struct mmc_bkops_info bkops_info; };/* include/linux/mmc/core.h +0
-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);Quoted 10 lines
Quoted 8 lines
include/linux/mmc/core.h +3
-0+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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 6 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Finally some overall thoughts. What I would like to understand is how we decide that the card has become "idle". I belive two values should be considered, but are they?
1. The card need BKOPS to be performed for some status level.
2. Request inactivity for a certain timeout has occured.Have you considered to use runtime PM for the card device instead of the workqueue?
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Maya,
Quoted 15 lines · On 4 December 2012 22:17, <[email protected]> wrote:
Hi Ulf,
Let me try to better explain:
The idea behind the periodic BKOPS is to check the card's need for BKOPS periodically in order to prevent an urgent BKOPS need by the card. In order to actually manage to prevent the urgent BKOPS need, the host should give the card enough time to perform the BKOPS (when it recognizes BKOPS need), otherwise there is no point in having the periodic BKOPS. The above results in the following:
1. After starting non-urgent BKOPS we "delay" getting into suspend by polling on the card's status (explanation below), in order to give the card time to perform the BKOPS. This has no effect on the power consumption since the same BKOPS operations that were performed after the foregound operation just moved to another location, meaning before going into suspend.
I am not sure what you are talking about here, runtime suspend or system suspend? Polling the card's status will not prevent any of this. So you have got this wrong.
Quoted 4 lines
2. Using
PM_SUSPEND_PREPAREinstead of the workqueue would not be efficient since we don't want to wait until the host is ready to get into suspend and then prevent him from suspending by doing BKOPS operations that can take a long time. It is better to start the BKOPS earlier.
I did not suggest to use PM_SUSPEND_PREPARE, but to use runtime PM for the card device. It can be an option to implement this feature on top of a workqueue. At least worth to consider.
Quoted 9 lines
I am not too familiar with the controllers code and also my understanding in runtime suspend is very basic, so feel free to correct me if I'm wrong here or the behavior in other controllers is different from
msm_sdcc.mmc_claim_hostcallshost->ops->enable. This API is implemented per controller but as far as I understand it, this API must prevent suspend, otherwise we might go into suspend while there is bus activity. By doingget_card_statuswe callmmc_claim_hostand this call is the one that "delays" getting into suspend.
host->ops->enable is the old way of implementing runtime power save for host drivers. Nowadays most drivers is using runtime PM instead.
When you say that mmc_claim_host will prevent suspend, I suppose you mean that host->ops->disable wont be called? That is definitely not the same as preventing a system suspend, and moreover it should not. If you think that the host must be prevented from entering runtime power save (runtime_supend or host->ops->disable), you must elaborate more on this, because I don't understand why this is needed.
Quoted 82 lines
If this is not the case in other controllers than the BKOPS will not prevent the suspend and BKOPS will be interrupted.
As for the effect on the battery consumption, this is probably something specific to our controller, so sorry if I created a confusion.Additional comments inline.
Thanks,
MayaQuoted 71 lines · Ulf Hansson
Quoted 17 lines · On 3 December 2012 10:49, <[email protected]> wrote:
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded.
I verified it with themsm_sdcccontroller.Chris - We do expect it to influence the battery consumption, since we now
delay getting into suspend (sincemmc_start_bkopswhich is called after the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten the timeout given topm_schedule_suspendby the periodic BKOPS idle time.
Does it make sense to you?Quoted 43 lines
Thanks,
MayaQuoted 40 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.)
Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance
is decreased.
Well, as Chris is mentioned, do you have any test plan?
So I will test more with this patch, because i want to test with dw-mmc controller, too.Quoted 25 lines · Maya Erez
Devices have various maintenance operations need to perform internally.
In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called
Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS
(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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.
What host controller suspend timeout are you referring to here?
If you are thinking of the runtime PM autosuspend timeout used in many host driver, then you might have missunderstand how runtime PM is used in host drivers.
This has nothing to do with BKOPS as such, unless you think that the card must be kept clocked during BKOPS operations, but then this needs to be stated somewhere in this patch and that is not the case.
Quoted 5 lines
Quoted 2 lines
Moreover, I could not find any new timeout added for the _host_ struct in this patch.
Yes, I was referring to the runtime PM autosuspend timeout. Since we want to give the BKOPS time to be performed before going into suspend, we need to take this timeout into account.
Not sure why need to consider this timeout. Anyway, if so you must instead use the runtime PM API to prevent the host from being runtime suspended, like pm_runtime_get_sync.
Quoted 565 lines
Quoted 11 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If BKOPS are 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.Not sure of what suspend you are talking about here. But for sure BKOPS must _never_ prevent a system suspend.
You might want to prevent a host from being runtime suspended though, but that is not accomplished in this patch.
This is explained in my general comment. Let me know if it is still not clear.
Quoted 216 lines
Quoted 216 lines
Quoted 216 lines
Quoted 54 lines
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.
Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_host
was moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the
function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger
for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +0
-0@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card;drivers/mmc/card/block.c static int mmc_blk_issue_rq(struct mmc_queue · line 1406
Quoted 10 lines
drivers/mmc/card/block.c +5
-1-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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkops = false; +mmc_stop_bkops (card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 146 lines
drivers/mmc/card/block.c +2
-0+ } + }ret = mmc_blk_part_switch(card, md); if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +15
-0@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between
drivers/mmc/core/core.c +37
-5+* fetching a request by the mmcqd and the delayed work, in case +* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)bool use_busy_signal;BUG_ON (!card); - -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception) { +pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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
drivers/mmc/core/core.c +4
-2+* for urgent BKOPS, BKOPs will be performed by the delayed BKOPs +* work, before going to suspend +*/ if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)
* bkops executed synchronously, otherwise
* the operation is in progress
*/drivers/mmc/core/core.c +7
-1-if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops);Quoted 12 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
+/**
+ *mmc_bkops_completion_polling()- Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.Not true! Suspend will not be prevented by doing a
mmc_send_status. Moreover, I would be interested to understand more about why you need to prevent "suspend". Please elaborate why you think this is needed.This is explained in my general comment. Let me know if it is still not clear.
Quoted 133 lines
Quoted 113 lines
Quoted 113 lines
Quoted 113 lines
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IFmmc_card_doing_bkopsin each
+ * iteration.
+ */drivers/mmc/core/core.c +11
-0+void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent
drivers/mmc/core/core.c +17
-0+* the host from getting into suspend +*/ + do { + mmc_claim_host(card->host); + + if (!mmc_card_doing_bkops(card)) + goto out; + + err = mmc_send_status(card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(card->host), err); + goto out; + } + + /* + * Some cards mishandle the status bits, so make sure tocheck
drivers/mmc/core/core.c +5
-0+* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { + pr_debug("%s: %s: completed BKOPs, exitpolling\n",
drivers/mmc/core/core.c +10
-0+ mmc_hostname(card->host), __func__); +mmc_card_clr_doing_bkops (card); + card->bkops_info.started_delayed_bkops = false; + goto out; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the
drivers/mmc/core/core.c +25
-0+* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed
drivers/mmc/core/core.c +12
-0+* BKOPS work in case the delayed work is executed on another CPU +*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already
drivers/mmc/core/core.c +8
-0+* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; }Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/core.c +1
-0+out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {Quoted 8 lines
drivers/mmc/core/core.c int mmc_pm_notify(struct notifier_block *notify_block, · line 2668
Quoted 8 lines
drivers/mmc/core/core.c +3
-2case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { +mmc_claim_host (host); err =mmc_stop_bkops (host->card); +mmc_release_host (host);This code seems a bit strange. You will check for
mmc_card_mmc, but not all (e)MMC will support bkops. How about acually just checking if bkops is "on" or "off" somehow.Quoted 5 lines
Additionally, so this piece of code shall stop an ongoing bkops before going to suspend, so that make sense. But would it not be more meaningfull to take care of that in
mmc_suspend_host? I mean why do yo need to do it in thePM_SUSPEND_PREPAREnotification sequence especially?This code was not added by me. I just added the mmc_claim_host(host)/mmc_release_host calls. Maybe Jaehoon, who added this code in the BKOPS patch can respond to your comment.
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
drivers/mmc/core/core.c +0
-1if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave(&host->lock, flags);drivers/mmc/core/mmc.c +0
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } }Quoted 12 lines
Quoted 6 lines
Quoted 6 lines
drivers/mmc/core/mmc.c static int mmc_init_card(struct mmc_host *host, u32 ocr, · line 1266
Quoted 6 lines
drivers/mmc/core/mmc.c +6
-0+if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling);I guess you don't have "removable" eMMC with BKOPS support so this code will in practice only be executed once for an eMMC, so we are safe. But still I am not fond of putting this code for workqueue here. Either that should be done as a part of when the card is created/deleted or when then host is created/deleted.
I will check if there could be a better place for this.
Quoted 48 lines
Quoted 48 lines
Quoted 48 lines
Quoted 48 lines
drivers/mmc/core/mmc.c +3
-0+ +/* +* 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
conditiondrivers/mmc/core/mmc.c +2
-0+* before starting BKOPs and going into suspend. +* If the host controller didn't set its idle time,
drivers/mmc/core/mmc.c +3
-0+* a default value is used. +*/ + card->bkops_info.delay_ms =
MMC_IDLE_BKOPS_TIME_MS;drivers/mmc/core/mmc.c +4
-0+if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms = min( + card->bkops_info.delay_ms, +card->bkops_info.host_suspend_tout_ms/2);
drivers/mmc/core/mmc.c +3
-0+ } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +13
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 228
Quoted 5 lines
include/linux/mmc/card.h +5
-0+* discard since the last idle BKOPS were scheduled +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms;As stated earlier, what is this timeout you are referring to? What suspend?
Explained above.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
include/linux/mmc/card.h +3
-0+unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */include/linux/mmc/card.h +3
-0+#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 241
Quoted 5 lines
include/linux/mmc/card.h +5
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed;Could not find "
sectors_changed" being used. Maybe you forgot to remove this from previous version of the patch.Yes, this should be removed.
Quoted 59 lines
Quoted 43 lines
Quoted 26 lines
Quoted 26 lines
include/linux/mmc/card.h +2
-0+}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */
unsigned int nr_parts;include/linux/mmc/card.h +2
-0+ +struct mmc_bkops_info bkops_info; };/* include/linux/mmc/core.h +0
-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);Quoted 10 lines
Quoted 8 lines
include/linux/mmc/core.h +3
-0+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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 6 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Finally some overall thoughts. What I would like to understand is how we decide that the card has become "idle". I belive two values should be considered, but are they?
1. The card need BKOPS to be performed for some status level.
2. Request inactivity for a certain timeout has occured.Have you considered to use runtime PM for the card device instead of the workqueue?
Quoted 7 lines
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Quoted 5 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kind regards
Ulf Hansson
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Ulf,
Sorry for the late response.
See my reply below.
Thanks,
Maya
Quoted 21 lines · Ulf Hansson
Hi Maya,
Quoted 17 lines · On 4 December 2012 22:17, <[email protected]> wrote:
Hi Ulf,
Let me try to better explain:
The idea behind the periodic BKOPS is to check the card's need for BKOPS periodically in order to prevent an urgent BKOPS need by the card. In order to actually manage to prevent the urgent BKOPS need, the host should give the card enough time to perform the BKOPS (when it recognizes
BKOPS need), otherwise there is no point in having the periodic BKOPS. The above results in the following:
1. After starting non-urgent BKOPS we "delay" getting into suspend by polling on the card's status (explanation below), in order to give the card time to perform the BKOPS. This has no effect on the power consumption since the same BKOPS operations that were performed after the
foregound operation just moved to another location, meaning before going into suspend.
Quoted 3 lines
I am not sure what you are talking about here, runtime suspend or system suspend? Polling the card's status will not prevent any of this. So you have got this wrong.
I am referring to the runtime suspend.
Our controller implements the runtime suspend and is not using the default implementation of core/bus.c.
This is the reason why in our implementation polling the card status "delays" the runtime suspend while it is not the case when using the default runtime suspend implementation.
I can try to explain here what our controller is doing but since it is specific to us then I guess it is not relevant to the discussion. Our controller is calling mmc_suspend_host in runtime suspend, which issues an HPI to stop the BKOPS.
Now that I understand that this code is not needed for all the host drivers I will add a flag to decide if polling is required when doing an unblocking BKOPS.
Other host drivers that actually suspend on runtime suspend can enable this flag and allow BKOPS to be active for a longer period.
I will prepare a new patch and send it for review.
Quoted 11 lines
Quoted 6 lines
2. Using
PM_SUSPEND_PREPAREinstead of the workqueue would not be efficient since we don't want to wait until the host is ready to get into
suspend and then prevent him from suspending by doing BKOPS operations that can take a long time. It is better to start the BKOPS earlier.I did not suggest to use
PM_SUSPEND_PREPARE, but to use runtime PM for the card device. It can be an option to implement this feature on top of a workqueue. At least worth to consider.
We consider to call mmc_start_bkops every time MMC becomes idle, to check the need for BKOPS. I will test it and include it in the next patch.
Quoted 15 lines
Quoted 11 lines
I am not too familiar with the controllers code and also my understanding
in runtime suspend is very basic, so feel free to correct me if I'm wrong
here or the behavior in other controllers is different frommsm_sdcc.mmc_claim_hostcallshost->ops->enable. This API is implemented per controller but as far as I understand it, this API must prevent suspend, otherwise we might go into suspend while there is bus activity. By doingget_card_statuswe callmmc_claim_hostand this call is the one that "delays" getting into suspend.
host->ops->enableis the old way of implementing runtime power save for host drivers. Nowadays most drivers is using runtime PM instead.
Quoted 7 lines
When you say that
mmc_claim_hostwill prevent suspend, I suppose you mean thathost->ops->disablewont be called? That is definitely not the same as preventing a system suspend, and moreover it should not. If you think that the host must be prevented from entering runtime power save (runtime_supendorhost->ops->disable), you must elaborate more on this, because I don't understand why this is needed.
Again, this is specific to our controller, so you can just ignore that.
Quoted 704 lines
Quoted 90 lines
If this is not the case in other controllers than the BKOPS will not prevent the suspend and BKOPS will be interrupted.
As for the effect on the battery consumption, this is probably something specific to our controller, so sorry if I created a confusion.Additional comments inline.
Thanks,
MayaQuoted 79 lines · Ulf Hansson
Quoted 19 lines · On 3 December 2012 10:49, <[email protected]> wrote:
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded.
I verified it with themsm_sdcccontroller.Chris - We do expect it to influence the battery consumption, since we now
delay getting into suspend (sincemmc_start_bkopswhich is called after
the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten
the timeout given topm_schedule_suspendby the periodic BKOPS idle time.
Does it make sense to you?Quoted 49 lines
Thanks,
MayaQuoted 46 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.)
Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance
is decreased.
Well, as Chris is mentioned, do you have any test plan? So I will test more with this patch, because i want to test with dw-mmc
controller, too.Quoted 30 lines · Maya Erez
Devices have various maintenance operations need to perform internally.
In order to reduce latencies during time critical operations like read
and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called
Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS
(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that
the device has enough time for its maintenance operations.This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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.What host controller suspend timeout are you referring to here?
If you are thinking of the runtime PM autosuspend timeout used in many host driver, then you might have missunderstand how runtime PM is used in host drivers.
This has nothing to do with BKOPS as such, unless you think that the card must be kept clocked during BKOPS operations, but then this needs to be stated somewhere in this patch and that is not the case.Quoted 7 lines
Quoted 2 lines
Moreover, I could not find any new timeout added for the _host_ struct in this patch.
Yes, I was referring to the runtime PM autosuspend timeout. Since we want
to give the BKOPS time to be performed before going into suspend, we need
to take this timeout into account.Not sure why need to consider this timeout. Anyway, if so you must instead use the runtime PM API to prevent the host from being runtime suspended, like
pm_runtime_get_sync.Quoted 588 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
If BKOPS are 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.Quoted 5 lines
Not sure of what suspend you are talking about here. But for sure BKOPS must _never_ prevent a system suspend.
You might want to prevent a host from being runtime suspended though, but that is not accomplished in this patch.
This is explained in my general comment. Let me know if it is still not clear.
Quoted 227 lines
Quoted 227 lines
Quoted 227 lines
Quoted 68 lines
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.Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_host
was moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if
an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the
function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger
for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +4
-1@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkopsQuoted 3 lines
=
false;
+mmc_stop_bkops(card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 150 lines
drivers/mmc/card/block.c +2
-0+ } + } ret =mmc_blk_part_switch (card, md);if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +15
-0@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between
+ * fetching a request by the mmcqd and the delayed work, in
casedrivers/mmc/core/core.c +12
-1+* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)bool use_busy_signal; BUG_ON(!card);drivers/mmc/core/core.c +4
-2- -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception){
drivers/mmc/core/core.c +20
-2+pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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
BKOPsdrivers/mmc/core/core.c +3
-2+* work, before going to suspend +*/ if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)
* bkops executed synchronously, otherwise
* the operation is in progress
*/drivers/mmc/core/core.c +7
-1-if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops);Quoted 12 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
+/**
+ *mmc_bkops_completion_polling()- Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.Not true! Suspend will not be prevented by doing a
mmc_send_status. Moreover, I would be interested to understand more about why you need to prevent "suspend". Please elaborate why you think this is needed.This is explained in my general comment. Let me know if it is still not clear.
Quoted 135 lines
Quoted 115 lines
Quoted 115 lines
Quoted 115 lines
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IFmmc_card_doing_bkopsin each
+ * iteration.
+ */drivers/mmc/core/core.c +11
-0+void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent
drivers/mmc/core/core.c +17
-0+* the host from getting into suspend +*/ + do { + mmc_claim_host(card->host); + + if (!mmc_card_doing_bkops(card)) + goto out; + + err = mmc_send_status(card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(card->host), err); + goto out; + } + + /* + * Some cards mishandle the status bits, so make sureto
checkdrivers/mmc/core/core.c +5
-0+* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { + pr_debug("%s: %s: completed BKOPs, exitpolling\n",
drivers/mmc/core/core.c +10
-0+ mmc_hostname(card->host), __func__); +mmc_card_clr_doing_bkops (card); + card->bkops_info.started_delayed_bkops = false; + goto out; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the
drivers/mmc/core/core.c +25
-0+* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed
+ * BKOPS work in case the delayed work is executed on another
CPUdrivers/mmc/core/core.c +11
-0+*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already
drivers/mmc/core/core.c +8
-0+* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; }Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/core.c +1
-0+out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {Quoted 8 lines
drivers/mmc/core/core.c int mmc_pm_notify(struct notifier_block *notify_block, · line 2668
Quoted 8 lines
drivers/mmc/core/core.c +3
-2case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { +mmc_claim_host (host); err =mmc_stop_bkops (host->card); +mmc_release_host (host);This code seems a bit strange. You will check for
mmc_card_mmc, but not all (e)MMC will support bkops. How about acually just checking if bkops is "on" or "off" somehow.Quoted 5 lines
Additionally, so this piece of code shall stop an ongoing bkops before going to suspend, so that make sense. But would it not be more meaningfull to take care of that in
mmc_suspend_host? I mean why do yo need to do it in thePM_SUSPEND_PREPAREnotification sequence especially?This code was not added by me. I just added the mmc_claim_host(host)/mmc_release_host calls. Maybe Jaehoon, who added this
code in the BKOPS patch can respond to your comment.Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
drivers/mmc/core/core.c +0
-1if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave(&host->lock, flags);drivers/mmc/core/mmc.c +0
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } }Quoted 13 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/mmc.c static int mmc_init_card(struct mmc_host *host, u32 ocr,
Quoted 7 lines
drivers/mmc/core/mmc.c +6
-0+if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling);I guess you don't have "removable" eMMC with BKOPS support so this code will in practice only be executed once for an eMMC, so we are safe. But still I am not fond of putting this code for workqueue here. Either that should be done as a part of when the card is created/deleted or when then host is created/deleted.
I will check if there could be a better place for this.
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
drivers/mmc/core/mmc.c +3
-0+ +/* +* 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,drivers/mmc/core/mmc.c +3
-0+* a default value is used. +*/ + card->bkops_info.delay_ms =
MMC_IDLE_BKOPS_TIME_MS;drivers/mmc/core/mmc.c +4
-0+if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms = min( + card->bkops_info.delay_ms, +card->bkops_info.host_suspend_tout_ms/2);
drivers/mmc/core/mmc.c +3
-0+ } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +13
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 228
Quoted 5 lines
include/linux/mmc/card.h +5
-0+* discard since the last idle BKOPS were scheduled +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms;As stated earlier, what is this timeout you are referring to? What suspend?
Explained above.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
include/linux/mmc/card.h +3
-0+unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */include/linux/mmc/card.h +3
-0+#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 241
Quoted 5 lines
include/linux/mmc/card.h +5
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed;Could not find "
sectors_changed" being used. Maybe you forgot to remove this from previous version of the patch.Yes, this should be removed.
Quoted 62 lines
Quoted 45 lines
Quoted 28 lines
Quoted 28 lines
include/linux/mmc/card.h +2
-0+}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */
unsigned int nr_parts;include/linux/mmc/card.h +2
-0+ +struct mmc_bkops_info bkops_info; };/* include/linux/mmc/core.h +1
-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);Quoted 10 lines
Quoted 8 lines
include/linux/mmc/core.h +2
-0+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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 7 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel"
in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Finally some overall thoughts. What I would like to understand is how we decide that the card has become "idle". I belive two values should be considered, but are they?
1. The card need BKOPS to be performed for some status level.
2. Request inactivity for a certain timeout has occured.Have you considered to use runtime PM for the card device instead of the workqueue?
Quoted 7 lines
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 6 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Quoted 30 lines · On 12 December 2012 13:32, <[email protected]> wrote:
Hi Ulf,
Sorry for the late response.
See my reply below.Thanks,
MayaQuoted 21 lines · Ulf Hansson
Hi Maya,
Quoted 17 lines · On 4 December 2012 22:17, <[email protected]> wrote:
Hi Ulf,
Let me try to better explain:
The idea behind the periodic BKOPS is to check the card's need for BKOPS periodically in order to prevent an urgent BKOPS need by the card. In order to actually manage to prevent the urgent BKOPS need, the host should give the card enough time to perform the BKOPS (when it recognizes
BKOPS need), otherwise there is no point in having the periodic BKOPS. The above results in the following:
1. After starting non-urgent BKOPS we "delay" getting into suspend by polling on the card's status (explanation below), in order to give the card time to perform the BKOPS. This has no effect on the power consumption since the same BKOPS operations that were performed after the
foregound operation just moved to another location, meaning before going into suspend.
Quoted 7 lines
Quoted 3 lines
I am not sure what you are talking about here, runtime suspend or system suspend? Polling the card's status will not prevent any of this. So you have got this wrong.
I am referring to the runtime suspend.
Our controller implements the runtime suspend and is not using the default implementation ofcore/bus.c.
This is not the "default runtime suspend" for a host device, but for the card device. Do not mix this up with runtime pm for a host device.
Right now runtime pm for the card _device_ is only enabled for SDIO. Thus SDIO drivers can use runtime pm to actually trigger an SDIO card to be fully suspended when it is not needed and thus save a lot of power. For example when a WLAN interface goes up/down.
Quoted 7 lines
This is the reason why in our implementation polling the card status "delays" the runtime suspend while it is not the case when using the default runtime suspend implementation.
I can try to explain here what our controller is doing but since it is specific to us then I guess it is not relevant to the discussion. Our controller is callingmmc_suspend_hostin runtime suspend, which issues an HPI to stop the BKOPS.
So, doing mmc_suspend_host in you runtime_suspend callback, is that really what you want to do?
1.
You will introduce significant latencies (I have seen SD-cards which needs more than 1 s to initialize, eMMC is better but we are anyway talking several 100 ms) once new requests arrives after the host as entered the runtime suspend state.
2.
SD cards has no "power off" notification, so you will actually stress test the SD cards internal FTL to be crash safe by cutting the power to it more often.
3.
You will prevent SD-cards from doing it's back ground operations, which is done automatically and not like in a controlled manner as for eMMC.
So of course, you save some power, but is the consequences worth it? :-)
Quoted 3 lines
Now that I understand that this code is not needed for all the host drivers I will add a flag to decide if polling is required when doing an unblocking BKOPS.
You must not poll to prevent this!
Instead you need to prevent the host from going into runtime suspend state, which is simply done by pm_runtime_get_sync for the host device.
Although, it _must_ not be done for drivers not doing mmc_suspend_host in their runtime suspend callbacks. Since then it will prevent these from doing runtime power save actions, which is not ok.
Quoted 749 lines
Other host drivers that actually suspend on runtime suspend can enable this flag and allow BKOPS to be active for a longer period. I will prepare a new patch and send it for review.
Quoted 11 lines
Quoted 6 lines
2. Using
PM_SUSPEND_PREPAREinstead of the workqueue would not be efficient since we don't want to wait until the host is ready to get into
suspend and then prevent him from suspending by doing BKOPS operations that can take a long time. It is better to start the BKOPS earlier.I did not suggest to use
PM_SUSPEND_PREPARE, but to use runtime PM for the card device. It can be an option to implement this feature on top of a workqueue. At least worth to consider.We consider to call
mmc_start_bkopsevery time MMC becomes idle, to check the need for BKOPS. I will test it and include it in the next patch.Quoted 15 lines
Quoted 11 lines
I am not too familiar with the controllers code and also my understanding
in runtime suspend is very basic, so feel free to correct me if I'm wrong
here or the behavior in other controllers is different frommsm_sdcc.mmc_claim_hostcallshost->ops->enable. This API is implemented per controller but as far as I understand it, this API must prevent suspend, otherwise we might go into suspend while there is bus activity. By doingget_card_statuswe callmmc_claim_hostand this call is the one that "delays" getting into suspend.
host->ops->enableis the old way of implementing runtime power save for host drivers. Nowadays most drivers is using runtime PM instead.Quoted 7 lines
When you say that
mmc_claim_hostwill prevent suspend, I suppose you mean thathost->ops->disablewont be called? That is definitely not the same as preventing a system suspend, and moreover it should not. If you think that the host must be prevented from entering runtime power save (runtime_supendorhost->ops->disable), you must elaborate more on this, because I don't understand why this is needed.Again, this is specific to our controller, so you can just ignore that.
Quoted 697 lines
Quoted 90 lines
If this is not the case in other controllers than the BKOPS will not prevent the suspend and BKOPS will be interrupted.
As for the effect on the battery consumption, this is probably something specific to our controller, so sorry if I created a confusion.Additional comments inline.
Thanks,
MayaQuoted 79 lines · Ulf Hansson
Quoted 19 lines · On 3 December 2012 10:49, <[email protected]> wrote:
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded.
I verified it with themsm_sdcccontroller.Chris - We do expect it to influence the battery consumption, since we now
delay getting into suspend (sincemmc_start_bkopswhich is called after
the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten
the timeout given topm_schedule_suspendby the periodic BKOPS idle time.
Does it make sense to you?Quoted 49 lines
Thanks,
MayaQuoted 46 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.)
Test controller is sdhci controller.
When i tested the performance with iozone, i didn't find that performance
is decreased.
Well, as Chris is mentioned, do you have any test plan? So I will test more with this patch, because i want to test with dw-mmc
controller, too.Quoted 30 lines · Maya Erez
Devices have various maintenance operations need to perform internally.
In order to reduce latencies during time critical operations like read
and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called
Background operations (BKOPS).
The device notifies the status of the BKOPS need by updatingBKOPS_STATUS
(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that
the device has enough time for its maintenance operations.This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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.What host controller suspend timeout are you referring to here?
If you are thinking of the runtime PM autosuspend timeout used in many host driver, then you might have missunderstand how runtime PM is used in host drivers.
This has nothing to do with BKOPS as such, unless you think that the card must be kept clocked during BKOPS operations, but then this needs to be stated somewhere in this patch and that is not the case.Quoted 7 lines
Quoted 2 lines
Moreover, I could not find any new timeout added for the _host_ struct in this patch.
Yes, I was referring to the runtime PM autosuspend timeout. Since we want
to give the BKOPS time to be performed before going into suspend, we need
to take this timeout into account.Not sure why need to consider this timeout. Anyway, if so you must instead use the runtime PM API to prevent the host from being runtime suspended, like
pm_runtime_get_sync.Quoted 588 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
If BKOPS are 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.Quoted 5 lines
Not sure of what suspend you are talking about here. But for sure BKOPS must _never_ prevent a system suspend.
You might want to prevent a host from being runtime suspended though, but that is not accomplished in this patch.
This is explained in my general comment. Let me know if it is still not clear.
Quoted 227 lines
Quoted 227 lines
Quoted 227 lines
Quoted 68 lines
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.Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_host
was moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if
an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside the
function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the trigger
for
checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +4
-1@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + card->bkops_info.started_delayed_bkopsQuoted 3 lines
=
false;
+mmc_stop_bkops(card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 150 lines
drivers/mmc/card/block.c +2
-0+ } + } ret =mmc_blk_part_switch (card, md);if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +15
-0@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between
+ * fetching a request by the mmcqd and the delayed work, in
casedrivers/mmc/core/core.c +12
-1+* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)bool use_busy_signal; BUG_ON(!card);drivers/mmc/core/core.c +4
-2- -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception){
drivers/mmc/core/core.c +20
-2+pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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
BKOPsdrivers/mmc/core/core.c +3
-2+* work, before going to suspend +*/ if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)
* bkops executed synchronously, otherwise
* the operation is in progress
*/drivers/mmc/core/core.c +7
-1-if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops);Quoted 12 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
+/**
+ *mmc_bkops_completion_polling()- Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.Not true! Suspend will not be prevented by doing a
mmc_send_status. Moreover, I would be interested to understand more about why you need to prevent "suspend". Please elaborate why you think this is needed.This is explained in my general comment. Let me know if it is still not clear.
Quoted 135 lines
Quoted 115 lines
Quoted 115 lines
Quoted 115 lines
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IFmmc_card_doing_bkopsin each
+ * iteration.
+ */drivers/mmc/core/core.c +11
-0+void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent
drivers/mmc/core/core.c +17
-0+* the host from getting into suspend +*/ + do { + mmc_claim_host(card->host); + + if (!mmc_card_doing_bkops(card)) + goto out; + + err = mmc_send_status(card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(card->host), err); + goto out; + } + + /* + * Some cards mishandle the status bits, so make sureto
checkdrivers/mmc/core/core.c +5
-0+* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { + pr_debug("%s: %s: completed BKOPs, exitpolling\n",
drivers/mmc/core/core.c +10
-0+ mmc_hostname(card->host), __func__); +mmc_card_clr_doing_bkops (card); + card->bkops_info.started_delayed_bkops = false; + goto out; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the
drivers/mmc/core/core.c +25
-0+* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed
+ * BKOPS work in case the delayed work is executed on another
CPUdrivers/mmc/core/core.c +11
-0+*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already
drivers/mmc/core/core.c +8
-0+* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; }Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/core.c +1
-0+out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {Quoted 8 lines
drivers/mmc/core/core.c int mmc_pm_notify(struct notifier_block · line 2669
Quoted 8 lines
drivers/mmc/core/core.c +3
-2case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { +mmc_claim_host (host); err =mmc_stop_bkops (host->card); +mmc_release_host (host);This code seems a bit strange. You will check for
mmc_card_mmc, but not all (e)MMC will support bkops. How about acually just checking if bkops is "on" or "off" somehow.Quoted 5 lines
Additionally, so this piece of code shall stop an ongoing bkops before going to suspend, so that make sense. But would it not be more meaningfull to take care of that in
mmc_suspend_host? I mean why do yo need to do it in thePM_SUSPEND_PREPAREnotification sequence especially?This code was not added by me. I just added the mmc_claim_host(host)/mmc_release_host calls. Maybe Jaehoon, who added this
code in the BKOPS patch can respond to your comment.Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
drivers/mmc/core/core.c +0
-1if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave(&host->lock, flags);drivers/mmc/core/mmc.c +0
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } }Quoted 13 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/mmc.c static int mmc_init_card(struct mmc_host · line 1269
Quoted 7 lines
drivers/mmc/core/mmc.c +6
-0+if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling);I guess you don't have "removable" eMMC with BKOPS support so this code will in practice only be executed once for an eMMC, so we are safe. But still I am not fond of putting this code for workqueue here. Either that should be done as a part of when the card is created/deleted or when then host is created/deleted.
I will check if there could be a better place for this.
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
drivers/mmc/core/mmc.c +3
-0+ +/* +* 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,drivers/mmc/core/mmc.c +3
-0+* a default value is used. +*/ + card->bkops_info.delay_ms =
MMC_IDLE_BKOPS_TIME_MS;drivers/mmc/core/mmc.c +4
-0+if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms = min( + card->bkops_info.delay_ms, +card->bkops_info.host_suspend_tout_ms/2);
drivers/mmc/core/mmc.c +3
-0+ } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +13
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 228
Quoted 5 lines
include/linux/mmc/card.h +5
-0+* discard since the last idle BKOPS were scheduled +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms;As stated earlier, what is this timeout you are referring to? What suspend?
Explained above.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
include/linux/mmc/card.h +3
-0+unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */include/linux/mmc/card.h +3
-0+#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h
Quoted 5 lines
include/linux/mmc/card.h +5
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed;Could not find "
sectors_changed" being used. Maybe you forgot to remove this from previous version of the patch.Yes, this should be removed.
Quoted 62 lines
Quoted 45 lines
Quoted 28 lines
Quoted 28 lines
include/linux/mmc/card.h +2
-0+}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */
unsigned int nr_parts;include/linux/mmc/card.h +2
-0+ +struct mmc_bkops_info bkops_info; };/* include/linux/mmc/core.h +1
-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);Quoted 10 lines
Quoted 8 lines
include/linux/mmc/core.h +2
-0+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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 7 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel"
in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Finally some overall thoughts. What I would like to understand is how we decide that the card has become "idle". I belive two values should be considered, but are they?
1. The card need BKOPS to be performed for some status level.
2. Request inactivity for a certain timeout has occured.Have you considered to use runtime PM for the card device instead of the workqueue?
Quoted 7 lines
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 6 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Quoted 7 lines
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 4 lines
--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Kind regards
Ulf Hansson
RE: [PATCH v3] mmc: core: Add support for idle time BKOPS
Hi Ulf,
Thanks for the info. We will consider our controller behavior in suspend. Would it be acceptable by you to keep the polling for BKOPS completion under a special CAPS2 flag?
Thanks,
Maya
-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Ulf Hansson
Sent: Thursday, December 13, 2012 12:18 PM
To: [email protected]
Cc: Jaehoon Chung; [email protected]; [email protected]; open list
Subject: Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
Quoted 29 lines · On 12 December 2012 13:32, <[email protected]> wrote:
Hi Ulf,
Sorry for the late response.
See my reply below.Thanks,
MayaQuoted 20 lines · Ulf Hansson
Hi Maya,
Quoted 16 lines · On 4 December 2012 22:17, <[email protected]> wrote:
Hi Ulf,
Let me try to better explain:
The idea behind the periodic BKOPS is to check the card's need for BKOPS periodically in order to prevent an urgent BKOPS need by the card. In order to actually manage to prevent the urgent BKOPS need, the host should give the card enough time to perform the BKOPS (when it recognizes BKOPS need), otherwise there is no point in having the periodic BKOPS.
The above results in the following:
1. After starting non-urgent BKOPS we "delay" getting into suspend by polling on the card's status (explanation below), in order to give the card time to perform the BKOPS. This has no effect on the power consumption since the same BKOPS operations that were performed after the foregound operation just moved to another location, meaning before going into suspend.
Quoted 7 lines
Quoted 3 lines
I am not sure what you are talking about here, runtime suspend or system suspend? Polling the card's status will not prevent any of this. So you have got this wrong.
I am referring to the runtime suspend.
Our controller implements the runtime suspend and is not using the default implementation ofcore/bus.c.
This is not the "default runtime suspend" for a host device, but for the card device. Do not mix this up with runtime pm for a host device.
Right now runtime pm for the card _device_ is only enabled for SDIO. Thus SDIO drivers can use runtime pm to actually trigger an SDIO card to be fully suspended when it is not needed and thus save a lot of power. For example when a WLAN interface goes up/down.
Quoted 7 lines
This is the reason why in our implementation polling the card status "delays" the runtime suspend while it is not the case when using the default runtime suspend implementation.
I can try to explain here what our controller is doing but since it is specific to us then I guess it is not relevant to the discussion. Our controller is callingmmc_suspend_hostin runtime suspend, which issues an HPI to stop the BKOPS.
So, doing mmc_suspend_host in you runtime_suspend callback, is that really what you want to do?
1.
You will introduce significant latencies (I have seen SD-cards which needs more than 1 s to initialize, eMMC is better but we are anyway talking several 100 ms) once new requests arrives after the host as entered the runtime suspend state.
2.
SD cards has no "power off" notification, so you will actually stress test the SD cards internal FTL to be crash safe by cutting the power to it more often.
3.
You will prevent SD-cards from doing it's back ground operations, which is done automatically and not like in a controlled manner as for eMMC.
So of course, you save some power, but is the consequences worth it? :-)
Quoted 3 lines
Now that I understand that this code is not needed for all the host drivers I will add a flag to decide if polling is required when doing an unblocking BKOPS.
You must not poll to prevent this!
Instead you need to prevent the host from going into runtime suspend state, which is simply done by pm_runtime_get_sync for the host device. Although, it _must_ not be done for drivers not doing mmc_suspend_host in their runtime suspend callbacks. Since then it will prevent these from doing runtime power save actions, which is not ok.
Quoted 33 lines
Other host drivers that actually suspend on runtime suspend can enable this flag and allow BKOPS to be active for a longer period. I will prepare a new patch and send it for review.
Quoted 11 lines
Quoted 6 lines
2. Using
PM_SUSPEND_PREPAREinstead of the workqueue would not be efficient since we don't want to wait until the host is ready to get into suspend and then prevent him from suspending by doing BKOPS operations that can take a long time. It is better to start the BKOPS earlier.I did not suggest to use
PM_SUSPEND_PREPARE, but to use runtime PM for the card device. It can be an option to implement this feature on top of a workqueue. At least worth to consider.We consider to call
mmc_start_bkopsevery time MMC becomes idle, to check the need for BKOPS. I will test it and include it in the next patch.Quoted 14 lines
Quoted 10 lines
I am not too familiar with the controllers code and also my understanding in runtime suspend is very basic, so feel free to correct me if I'm wrong here or the behavior in other controllers is different from
msm_sdcc.mmc_claim_hostcallshost->ops->enable. This API is implemented per controller but as far as I understand it, this API must prevent suspend, otherwise we might go into suspend while there is bus activity. By doingget_card_statuswe callmmc_claim_hostand this call is the one that "delays" getting into suspend.
host->ops->enableis the old way of implementing runtime power save for host drivers. Nowadays most drivers is using runtime PM instead.
Quoted 87 lines
Quoted 7 lines
When you say that
mmc_claim_hostwill prevent suspend, I suppose you mean thathost->ops->disablewont be called? That is definitely not the same as preventing a system suspend, and moreover it should not. If you think that the host must be prevented from entering runtime power save (runtime_supendorhost->ops->disable), you must elaborate more on this, because I don't understand why this is needed.Again, this is specific to our controller, so you can just ignore that.
Quoted 78 lines
Quoted 78 lines
If this is not the case in other controllers than the BKOPS will not prevent the suspend and BKOPS will be interrupted.
As for the effect on the battery consumption, this is probably something specific to our controller, so sorry if I created a confusion.Additional comments inline.
Thanks,
MayaQuoted 67 lines · Ulf Hansson
Quoted 17 lines · On 3 December 2012 10:49, <[email protected]> wrote:
Hi Jaehoon,
With this patch we don't expect to see any degradation. Thanks for verifying that.
The test plan would be to run the lmdd and iozone benchmarks with this patch and verify that the performance is not degraded. I verified it with themsm_sdcccontroller.Chris - We do expect it to influence the battery consumption, since we now delay getting into suspend (since
mmc_start_bkopswhich is called after the delayed work is executed claims the host).
The solution for that should be done by the controller which can shorten the timeout given topm_schedule_suspendby the periodic BKOPS idle time.
Does it make sense to you?Quoted 40 lines
Thanks,
MayaQuoted 37 lines · Jaehoon Chung
Hi Maya,
Thank you a lot for working idle time BKOPS.
I tested with this patch. It's working fine.(Suspend/resume is also working well.) Test controller is sdhci controller. When i tested the performance with iozone, i didn't find that performance is decreased.
Well, as Chris is mentioned, do you have any test plan? So I will test more with this patch, because i want to test with dw-mmc controller, too.Quoted 24 lines · Maya Erez
Devices have various maintenance operations need to perform internally.
In order to reduce latencies during time critical operations like read and write, it is better to execute maintenance operations in other times - when the host is not being serviced. Such operations are called Background operations (BKOPS). The device notifies the status of the BKOPS need by updatingBKOPS_STATUS(EXT_CSDbyte [246]).According to the standard a host that supports BKOPS shall check the status periodically and start background operations as needed, so that the device has enough time for its maintenance operations.
This patch adds support for this periodic check of the BKOPS status. Since foreground operations are of higher priority than background operations the host will check the need for BKOPS when it is idle, and in case of an incoming request the BKOPS operation will be interrupted.
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.
What host controller suspend timeout are you referring to here?
If you are thinking of the runtime PM autosuspend timeout used in many host driver, then you might have missunderstand how runtime PM is used in host drivers.
This has nothing to do with BKOPS as such, unless you think that the card must be kept clocked during BKOPS operations, but then this needs to be stated somewhere in this patch and that is not the
case.
Quoted 608 lines
Quoted 599 lines
Quoted 6 lines
Quoted 3 lines
Moreover, I could not find any new timeout added for the _host_ struct in this patch.
Yes, I was referring to the runtime PM autosuspend timeout. Since we want to give the BKOPS time to be performed before going into suspend, we need to take this timeout into account.
Not sure why need to consider this timeout. Anyway, if so you must instead use the runtime PM API to prevent the host from being runtime suspended, like
pm_runtime_get_sync.Quoted 581 lines
Quoted 10 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
If BKOPS are 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.
Not sure of what suspend you are talking about here. But for sure BKOPS must _never_ prevent a system suspend.
You might want to prevent a host from being runtime suspended though, but that is not accomplished in this patch.
This is explained in my general comment. Let me know if it is still not clear.
Quoted 222 lines
Quoted 222 lines
Quoted 222 lines
Quoted 64 lines
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.Signed-off-by Maya Erez <[email protected]>---
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
- Sincemmc_start_bkopsis called from two contexts now,mmc_claim_hostwas moved to the beginning of the function
- Also, the check ofdoing_bkopsshould be protected when determing if an HPI is needed due to the same reason.Changes in v3:
- Move the call tostop_bkopstoblock.c.
This allows us to remove themmc_claim_hostfrom inside
the function and doesn't cause additional degradation
due to un-neccessary calim host operationChanges in v2:
- Check the number of written / discarded sectors as the
trigger for checking the BKOPS need.
- Code review fixes--- drivers/mmc/card/block.c | 8 ++- drivers/mmc/card/queue.c | 2 + drivers/mmc/core/core.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 23 ++++++ include/linux/mmc/card.h | 35 +++++++++ include/linux/mmc/core.h | 3 + 6 files changed, 237 insertions(+), 12 deletions(-)drivers/mmc/card/block.c +5
-1@@ -1394,9 +1394,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card; -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 && + card->bkops_info.started_delayed_bkops) { + + card->bkops_info.started_delayed_bkopsQuoted 3 lines
=
false;
+mmc_stop_bkops(card);We didn't need to check whether
mmc_stop_bkopsis success or not? Ifmmc_stop_bkops()is failed, then bkops is continuously running.Best Regards,
Jaehoon ChungQuoted 149 lines
drivers/mmc/card/block.c +2
-0+ } + } ret =mmc_blk_part_switch (card, md);if (ret) {drivers/mmc/card/queue.c +2
-0@@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d) {struct mmc_queue *mq = d;struct request_queue *q = mq->queue; +struct mmc_card *card = mq->card; current->flags |=PF_MEMALLOC ;@@ -83,6 +84,7 @@ static int mmc_queue_thread(void *d)set_current_state (TASK_RUNNING );break ; } +mmc_start_delayed_bkops (card);up (&mq->thread_sem);schedule ();down (&mq->thread_sem);drivers/mmc/core/core.c +15
-0@@ -253,9 +253,36 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) }/** +* mmc_start_delayed_bkops() - Start a delayed work to check for +* the need of non urgent BKOPS +* +* @card: MMC card to start BKOPS on +*/ +void mmc_start_delayed_bkops (struct mmc_card *card) +{ +if (!card || !card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +return ; + +pr_debug ("%s: %s: queueing delayed_bkops_work\n" , +mmc_hostname (card->host), __func__); + +/* +* cancel_delayed_bkops_work will prevent a race condition between
+ * fetching a request by the mmcqd and the delayed work, in
casedrivers/mmc/core/core.c +12
-1+* 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)); +} +EXPORT_SYMBOL (mmc_start_delayed_bkops); + +/** * mmc_start_bkops - start BKOPS for supported cards * @card: MMC card to start BKOPS -* @form_exception: A flag to indicate if this function was +* @from_exception: A flag to indicate if this function was * called due to an exception raised by the card * * Start background operations whenever requested. @@ -269,25 +296,47 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)bool use_busy_signal; BUG_ON(!card);drivers/mmc/core/core.c +4
-2- -if (!card->ext_csd.bkops_en ||mmc_card_doing_bkops (card)) +if (!card->ext_csd.bkops_en)return ; +mmc_claim_host (card->host); + +if ((card->bkops_info.cancel_delayed_work) && !from_exception){
drivers/mmc/core/core.c +20
-2+pr_debug ("%s: %s: cancel_delayed_work was set, exit\n" , +mmc_hostname (card->host), __func__); + card->bkops_info.cancel_delayed_work = false; + goto out; + } + +if (mmc_card_doing_bkops (card)) { +pr_debug ("%s: %s: already doing bkops, exit\n" , +mmc_hostname (card->host), __func__); + 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); -return ; + goto out; }if (!card->ext_csd.raw_bkops_status) -return ; + 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
BKOPsdrivers/mmc/core/core.c +3
-2+* work, before going to suspend +*/ if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 && from_exception) -return ; + goto out; -mmc_claim_host (card->host);if (card->ext_csd.raw_bkops_status >=EXT_CSD_BKOPS_LEVEL_2 ) { timeout =MMC_BKOPS_MAX_TIMEOUT ; use_busy_signal = true;@@ -309,13 +358,108 @@ void mmc_start_bkops(struct mmc_card *card,bool
from_exception)
* bkops executed synchronously, otherwise
* the operation is in progress
*/drivers/mmc/core/core.c +7
-1-if (!use_busy_signal) +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); + } + out:mmc_release_host (card->host); }EXPORT_SYMBOL (mmc_start_bkops);Quoted 12 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
+/**
+ *mmc_bkops_completion_polling()- Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.Not true! Suspend will not be prevented by doing a
mmc_send_status. Moreover, I would be interested to understand more about why you need to prevent "suspend". Please elaborate why you think this is needed.This is explained in my general comment. Let me know if it is still not clear.
Quoted 135 lines
Quoted 115 lines
Quoted 115 lines
Quoted 115 lines
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IFmmc_card_doing_bkopsin each
+ * iteration.
+ */drivers/mmc/core/core.c +11
-0+void mmc_bkops_completion_polling (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.poll_for_completion); +unsigned long timeout_jiffies = jiffies + +msecs_to_jiffies (BKOPS_COMPLETION_POLLING_TIMEOUT_MS ); +u32 status; +int err; + +/* +* Wait for the BKOPs to complete. Keep reading the status to prevent
drivers/mmc/core/core.c +17
-0+* the host from getting into suspend +*/ + do { + mmc_claim_host(card->host); + + if (!mmc_card_doing_bkops(card)) + goto out; + + err = mmc_send_status(card, &status); + if (err) { + pr_err("%s: error %d requesting status\n", + mmc_hostname(card->host), err); + goto out; + } + + /* + * Some cards mishandle the status bits, so make sureto
checkdrivers/mmc/core/core.c +5
-0+* both the busy indication and the card state. +*/ +if ((status &R1_READY_FOR_DATA ) && + (R1_CURRENT_STATE (status) !=R1_STATE_PRG )) { + pr_debug("%s: %s: completed BKOPs, exitpolling\n",
drivers/mmc/core/core.c +10
-0+ mmc_hostname(card->host), __func__); +mmc_card_clr_doing_bkops (card); + card->bkops_info.started_delayed_bkops = false; + goto out; + } + +mmc_release_host (card->host); + +/* +* Sleep before checking the card status again to allow the
drivers/mmc/core/core.c +25
-0+* card to complete the BKOPs operation +*/ +msleep (BKOPS_COMPLETION_POLLING_INTERVAL_MS ); + }while (time_before (jiffies, timeout_jiffies)); + +pr_err ("%s: %s: exit polling due to timeout\n" , +mmc_hostname (card->host), __func__); + +return ; +out: +mmc_release_host (card->host); +} + +/** +* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is +* needed +* @work: The idle time BKOPS work +*/ +void mmc_start_idle_time_bkops (struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, + bkops_info.dw.work); + +/* +* Prevent a race condition between mmc_stop_bkops and the delayed
+ * BKOPS work in case the delayed work is executed on another
CPUdrivers/mmc/core/core.c +11
-0+*/ +if (card->bkops_info.cancel_delayed_work) +return ; + +mmc_start_bkops (card, false); +} +EXPORT_SYMBOL (mmc_start_idle_time_bkops); +static void mmc_wait_done (struct mmc_request *mrq) {complete (&mrq->completion);@@ -582,6 +726,17 @@ int mmc_stop_bkops(struct mmc_card *card)int err =0 ;BUG_ON (!card); + +/* +* Notify the delayed work to be cancelled, in case it was already
drivers/mmc/core/core.c +8
-0+* removed from the queue, but was not started yet +*/ + card->bkops_info.cancel_delayed_work = true; +if (delayed_work_pending (&card->bkops_info.dw)) +cancel_delayed_work_sync (&card->bkops_info.dw); +if (!mmc_card_doing_bkops (card)) + goto out; + err =mmc_interrupt_hpi (card);/* @@ -593,6 +748,7 @@ int mmc_stop_bkops(struct mmc_card *card) err =0 ; }Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/core.c +1
-0+out:return err; }EXPORT_SYMBOL (mmc_stop_bkops);@@ -2506,15 +2662,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) {Quoted 8 lines
drivers/mmc/core/core.c int mmc_pm_notify(struct notifier_block · line 2669
Quoted 8 lines
drivers/mmc/core/core.c +3
-2case PM_HIBERNATION_PREPARE :case PM_SUSPEND_PREPARE : -if (host->card &&mmc_card_mmc (host->card) && -mmc_card_doing_bkops (host->card)) { +if (host->card &&mmc_card_mmc (host->card)) { +mmc_claim_host (host); err =mmc_stop_bkops (host->card); +mmc_release_host (host);This code seems a bit strange. You will check for
mmc_card_mmc, but not all (e)MMC will support bkops. How about acually just checking if bkops is "on" or "off" somehow.Quoted 5 lines
Additionally, so this piece of code shall stop an ongoing bkops before going to suspend, so that make sense. But would it not be more meaningfull to take care of that in
mmc_suspend_host? I mean why do yo need to do it in thePM_SUSPEND_PREPAREnotification sequence especially?This code was not added by me. I just added the mmc_claim_host(host)/mmc_release_host calls. Maybe Jaehoon, who added this
code in the BKOPS patch can respond to your comment.Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
drivers/mmc/core/core.c +0
-1if (err) {pr_err ("%s: didn't stop bkops\n" ,mmc_hostname (host));return err; } -mmc_card_clr_doing_bkops (host->card); }spin_lock_irqsave(&host->lock, flags);drivers/mmc/core/mmc.c +0
-0@@ -1258,6 +1258,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } }Quoted 13 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/core/mmc.c static int mmc_init_card(struct mmc_host · line 1269
Quoted 7 lines
drivers/mmc/core/mmc.c +6
-0+if (!oldcard) { +if (card->ext_csd.bkops_en) { +INIT_DELAYED_WORK (&card->bkops_info.dw, + mmc_start_idle_time_bkops); +INIT_WORK (&card->bkops_info.poll_for_completion, + mmc_bkops_completion_polling);I guess you don't have "removable" eMMC with BKOPS support so this code will in practice only be executed once for an eMMC, so we are safe. But still I am not fond of putting this code for workqueue here. Either that should be done as a part of when the card is created/deleted or when then host is created/deleted.
I will check if there could be a better place for this.
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
drivers/mmc/core/mmc.c +3
-0+ +/* +* 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,drivers/mmc/core/mmc.c +3
-0+* a default value is used. +*/ + card->bkops_info.delay_ms =
MMC_IDLE_BKOPS_TIME_MS;drivers/mmc/core/mmc.c +4
-0+if (card->bkops_info.host_suspend_tout_ms) + card->bkops_info.delay_ms = min( + card->bkops_info.delay_ms, +card->bkops_info.host_suspend_tout_ms/2);
drivers/mmc/core/mmc.c +3
-0+ } + } +if (!oldcard) host->card = card;include/linux/mmc/card.h +13
-0@@ -208,6 +208,39 @@ struct mmc_part {#define MMC_BLK_DATA_AREA_GP (1<<2) }; +/** +* struct mmc_bkops_info - BKOPS data +* @dw: Idle time bkops delayed work +* @host_suspend_tout_ms: The host controller idle time, +* before getting into suspend +* @delay_ms: The time to start the BKOPS +* delayed work once MMC thread is idle +* @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 Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h struct mmc_part { · line 228
Quoted 5 lines
include/linux/mmc/card.h +5
-0+* discard since the last idle BKOPS were scheduled +*/ +struct mmc_bkops_info { +struct delayed_work dw; +unsigned int host_suspend_tout_ms;As stated earlier, what is this timeout you are referring to? What suspend?
Explained above.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
include/linux/mmc/card.h +3
-0+unsigned int delay_ms; +/* +* A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */include/linux/mmc/card.h +3
-0+#define MMC_IDLE_BKOPS_TIME_MS 2000 +struct work_struct poll_for_completion; +/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
include/linux/mmc/card.h
Quoted 5 lines
include/linux/mmc/card.h +5
-0+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000/* in ms */ +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000/* in ms */ +bool cancel_delayed_work; +bool started_delayed_bkops; +unsigned int sectors_changed;Could not find "
sectors_changed" being used. Maybe you forgot to remove this from previous version of the patch.Yes, this should be removed.
Quoted 62 lines
Quoted 45 lines
Quoted 28 lines
Quoted 28 lines
include/linux/mmc/card.h +2
-0+}; +/* * MMC device */ @@ -276,6 +309,8 @@ struct mmc_card {struct dentry *debugfs_root;struct mmc_part part[MMC_NUM_PHY_PARTITION ];/* physical partitions */
unsigned int nr_parts;include/linux/mmc/card.h +2
-0+ +struct mmc_bkops_info bkops_info; };/* include/linux/mmc/core.h +1
-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);Quoted 10 lines
Quoted 8 lines
include/linux/mmc/core.h +2
-0+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 intmmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 7 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel"
in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Finally some overall thoughts. What I would like to understand is how we decide that the card has become "idle". I belive two values should be considered, but are they?
1. The card need BKOPS to be performed for some status level.
2. Request inactivity for a certain timeout has occured.Have you considered to use runtime PM for the card device instead of the workqueue?
Quoted 7 lines
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux FoundationQuoted 6 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/Quoted 7 lines
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 4 lines
--
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html