All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC/PATCH] mmc: core: Add support for idle time BKOPs
@ 2012-08-05 13:08   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-08-05 13:08 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, linux-arm-msm, Maya Erez, Jaehoon Chung, open list

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 <merez@codeaurora.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
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(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f1c84de..4519271 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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) {
 			/*
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index e360a97..c9e1cee 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ed2cc93..14830d4 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -46,6 +46,15 @@
  * operations the card has to peform
  */
 #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
 
 static 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)
 }
 
 /**
+ *      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 (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));
+
+	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);
+			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__);
+			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);
 
 	/*
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 597f189..d328191 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -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);
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 342fe84..835d6c8 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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;
 };
 
 /*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..665d345 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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);
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f578a71..8aaaf1d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -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;
 };
 
-- 
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.

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [RFC/PATCH] mmc: core: Add support for idle time BKOPs
@ 2012-08-05 13:08   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-08-05 13:08 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, linux-arm-msm, Maya Erez, Jaehoon Chung, open list

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 <merez@codeaurora.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
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(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f1c84de..4519271 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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) {
 			/*
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index e360a97..c9e1cee 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ed2cc93..14830d4 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -46,6 +46,15 @@
  * operations the card has to peform
  */
 #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
 
 static 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)
 }
 
 /**
+ *      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 (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));
+
+	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);
+			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__);
+			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);
 
 	/*
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 597f189..d328191 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -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);
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 342fe84..835d6c8 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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;
 };
 
 /*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..665d345 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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);
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f578a71..8aaaf1d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -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;
 };
 
-- 
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.

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [RFC/PATCH] mmc: core: Add support for idle time BKOPs
  2012-08-05 13:08   ` Maya Erez
  (?)
@ 2012-08-30  7:36   ` Jaehoon Chung
  2012-09-04  5:42       ` merez
  -1 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2012-08-30  7:36 UTC (permalink / raw)
  To: Maya Erez; +Cc: cjb, linux-mmc, linux-arm-msm, Jaehoon Chung, open list

On 08/05/2012 10:08 PM, Maya Erez wrote:
> 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 <merez@codeaurora.org>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
> 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(-)
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index f1c84de..4519271 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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) {
>  			/*
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index e360a97..c9e1cee 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index ed2cc93..14830d4 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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 */
> +#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?
>  
>  static 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)
>  }
>  
>  /**
> + *      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?
> +
> +	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.
> +
> +	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);
> +			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.
> +			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);
>  
>  	/*
> diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
> index 597f189..d328191 100644
> --- a/drivers/mmc/core/host.c
> +++ b/drivers/mmc/core/host.c
> @@ -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);
>  
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 342fe84..835d6c8 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>  
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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);
>  
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index f578a71..8aaaf1d 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -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;
>  };
>  
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [RFC/PATCH] mmc: core: Add support for idle time BKOPs
  2012-08-30  7:36   ` Jaehoon Chung
@ 2012-09-04  5:42       ` merez
  0 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-09-04  5:42 UTC (permalink / raw)
  Cc: Maya Erez, cjb, linux-mmc, linux-arm-msm, Jaehoon Chung, open list


On Thu, August 30, 2012 12:36 am, Jaehoon Chung wrote:
> On 08/05/2012 10:08 PM, Maya Erez wrote:
>> 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 <merez@codeaurora.org>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> ---
>> 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(-)
>>
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index f1c84de..4519271 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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) {
>>  			/*
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..c9e1cee 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index ed2cc93..14830d4 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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 */
>> +#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?
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).
>>
>>  static 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)
>>  }
>>
>>  /**
>> + *      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?
>> +
>> +	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.
Will fix that.
>> +
>> +	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);
>> +			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.
Will fix that.
>> +			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);
>>
>>  	/*
>> diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
>> index 597f189..d328191 100644
>> --- a/drivers/mmc/core/host.c
>> +++ b/drivers/mmc/core/host.c
>> @@ -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);
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 342fe84..835d6c8 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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);
>>
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index f578a71..8aaaf1d 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -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;
>>  };
>>
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
Thanks,
Maya

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [RFC/PATCH] mmc: core: Add support for idle time BKOPs
@ 2012-09-04  5:42       ` merez
  0 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-09-04  5:42 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Maya Erez, cjb, linux-mmc, linux-arm-msm, Jaehoon Chung, open list


On Thu, August 30, 2012 12:36 am, Jaehoon Chung wrote:
> On 08/05/2012 10:08 PM, Maya Erez wrote:
>> 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 <merez@codeaurora.org>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> ---
>> 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(-)
>>
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index f1c84de..4519271 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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) {
>>  			/*
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..c9e1cee 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index ed2cc93..14830d4 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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 */
>> +#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?
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).
>>
>>  static 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)
>>  }
>>
>>  /**
>> + *      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?
>> +
>> +	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.
Will fix that.
>> +
>> +	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);
>> +			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.
Will fix that.
>> +			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);
>>
>>  	/*
>> diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
>> index 597f189..d328191 100644
>> --- a/drivers/mmc/core/host.c
>> +++ b/drivers/mmc/core/host.c
>> @@ -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);
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 342fe84..835d6c8 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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);
>>
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index f578a71..8aaaf1d 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -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;
>>  };
>>
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
Thanks,
Maya

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v2] mmc: core: Add support for idle time BKOPS
@ 2012-10-04 22:28   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-10-04 22:28 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-arm-msm, Maya Erez, Jaehoon Chung, open list

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.

Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
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.
---
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..ed040d5 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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) {
 			/*
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index e360a97..e96f5cf 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6612163..fd8783d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 7509de1..f8ff640 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -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;
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 78cc3be..0e4d55a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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;
 };
 
 /*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..665d345 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH v2] mmc: core: Add support for idle time BKOPS
@ 2012-10-04 22:28   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-10-04 22:28 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-arm-msm, Maya Erez, Jaehoon Chung, open list

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.

Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
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.
---
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..ed040d5 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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) {
 			/*
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index e360a97..e96f5cf 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6612163..fd8783d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 7509de1..f8ff640 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -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;
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 78cc3be..0e4d55a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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;
 };
 
 /*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..665d345 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
  2012-10-04 22:28   ` Maya Erez
@ 2012-10-09  3:52     ` merez
  -1 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-10-09  3:52 UTC (permalink / raw)
  Cc: linux-mmc, linux-arm-msm, Maya Erez, Jaehoon Chung, open list

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
On Thu, October 4, 2012 3:28 pm, Maya Erez wrote:
> 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.
>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
> 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.
> ---
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..ed040d5 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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) {
>  			/*
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index e360a97..e96f5cf 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 6612163..fd8783d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7509de1..f8ff640 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 78cc3be..0e4d55a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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 majordomo@vger.kernel.org
> 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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
@ 2012-10-09  3:52     ` merez
  0 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-10-09  3:52 UTC (permalink / raw)
  To: Maya Erez; +Cc: linux-mmc, linux-arm-msm, Maya Erez, Jaehoon Chung, open list

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
On Thu, October 4, 2012 3:28 pm, Maya Erez wrote:
> 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.
>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
> 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.
> ---
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..ed040d5 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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) {
>  			/*
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index e360a97..e96f5cf 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 6612163..fd8783d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7509de1..f8ff640 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 78cc3be..0e4d55a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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 majordomo@vger.kernel.org
> 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


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
  2012-10-04 22:28   ` Maya Erez
  (?)
  (?)
@ 2012-10-16  7:53   ` Jaehoon Chung
  2012-11-07  4:28       ` merez
  -1 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2012-10-16  7:53 UTC (permalink / raw)
  To: Maya Erez; +Cc: linux-mmc, linux-arm-msm, Jaehoon Chung, open list

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 Chung

> ---
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..ed040d5 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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?
> +
>  	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.
> +
>  	do {
>  		if (rqc) {
>  			/*
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index e360a97..e96f5cf 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
if mmc_stop_bkops is failed..?
> +			}
> +
>  			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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 6612163..fd8783d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7509de1..f8ff640 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>  
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 78cc3be..0e4d55a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>  
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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);
>  
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars
@ 2012-10-18 16:31 ` Sangho Yi
  2012-10-18 16:31   ` [PATCH 2/6] mmc: core: sdio_bus.c: Removed trailing whitespace error Sangho Yi
                     ` (5 more replies)
  0 siblings, 6 replies; 88+ messages in thread
From: Sangho Yi @ 2012-10-18 16:31 UTC (permalink / raw)
  To: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb
  Cc: linux-kernel, linux-mmc, Sangho Yi

I fixed lines over 80 characters per line.

Signed-off-by: Sangho Yi <antiroot@gmail.com>
---
 drivers/mmc/core/sdio_io.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 8f6f5ac..c5f53d8 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -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, &reg);
+		ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0,
+				&reg);
 		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;
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 2/6] mmc: core: sdio_bus.c: Removed trailing whitespace error
  2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
@ 2012-10-18 16:31   ` Sangho Yi
  2012-10-18 16:31   ` [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars Sangho Yi
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 88+ messages in thread
From: Sangho Yi @ 2012-10-18 16:31 UTC (permalink / raw)
  To: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb
  Cc: linux-kernel, linux-mmc, Sangho Yi

Removed the one unnecessary whitespace error from sdio_bus.c

Signed-off-by: Sangho Yi <antiroot@gmail.com>
---
 drivers/mmc/core/sdio_bus.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 6bf6879..13146d2 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -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;
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars
  2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
  2012-10-18 16:31   ` [PATCH 2/6] mmc: core: sdio_bus.c: Removed trailing whitespace error Sangho Yi
@ 2012-10-18 16:31   ` Sangho Yi
  2012-10-18 23:54     ` Jaehoon Chung
  2012-10-18 16:31   ` [PATCH 4/6] mmc: core: sdio_bus.c: Removed space between func name and () Sangho Yi
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 88+ messages in thread
From: Sangho Yi @ 2012-10-18 16:31 UTC (permalink / raw)
  To: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb
  Cc: linux-kernel, linux-mmc, Sangho Yi

I fixed lines over 80 characters per line.

Signed-off-by: Sangho Yi <antiroot@gmail.com>
---
 drivers/mmc/core/sdio_bus.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 13146d2..efa30eb 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -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);
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 4/6] mmc: core: sdio_bus.c: Removed space between func name and ()
  2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
  2012-10-18 16:31   ` [PATCH 2/6] mmc: core: sdio_bus.c: Removed trailing whitespace error Sangho Yi
  2012-10-18 16:31   ` [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars Sangho Yi
@ 2012-10-18 16:31   ` Sangho Yi
  2012-10-18 16:31   ` [PATCH 5/6] mmc: core: sdio_bus.c: Fixed a warning for the pr_warning( Sangho Yi
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 88+ messages in thread
From: Sangho Yi @ 2012-10-18 16:31 UTC (permalink / raw)
  To: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb
  Cc: linux-kernel, linux-mmc, Sangho Yi

Fixed a coding style warning like this:
from foo (arg) --> to foo(arg)

Signed-off-by: Sangho Yi <antiroot@gmail.com>
---
 drivers/mmc/core/sdio_bus.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index efa30eb..1a1d96c 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -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);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 5/6] mmc: core: sdio_bus.c: Fixed a warning for the pr_warning(...
  2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
                     ` (2 preceding siblings ...)
  2012-10-18 16:31   ` [PATCH 4/6] mmc: core: sdio_bus.c: Removed space between func name and () Sangho Yi
@ 2012-10-18 16:31   ` Sangho Yi
  2012-10-18 16:31   ` [PATCH 6/6] mmc: core: sdio_bus.c: Removed unnecessary NULL check routine Sangho Yi
  2012-10-18 16:47   ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Joe Perches
  5 siblings, 0 replies; 88+ messages in thread
From: Sangho Yi @ 2012-10-18 16:31 UTC (permalink / raw)
  To: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb
  Cc: linux-kernel, linux-mmc, Sangho Yi

I fixed a line which had the following warning:
WARNING: Prefer pr_warn(... to pr_warning(...

Signed-off-by: Sangho Yi <antiroot@gmail.com>
---
 drivers/mmc/core/sdio_bus.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 1a1d96c..e5b7197 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -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);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 6/6] mmc: core: sdio_bus.c: Removed unnecessary NULL check routine
  2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
                     ` (3 preceding siblings ...)
  2012-10-18 16:31   ` [PATCH 5/6] mmc: core: sdio_bus.c: Fixed a warning for the pr_warning( Sangho Yi
@ 2012-10-18 16:31   ` Sangho Yi
  2012-10-18 16:47   ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Joe Perches
  5 siblings, 0 replies; 88+ messages in thread
From: Sangho Yi @ 2012-10-18 16:31 UTC (permalink / raw)
  To: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb
  Cc: linux-kernel, linux-mmc, Sangho Yi

kfree(something) is safe function, so I removed the NULL check routine
for the input of kfree.

Signed-off-by: Sangho Yi <antiroot@gmail.com>
---
 drivers/mmc/core/sdio_bus.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index e5b7197..cf02146 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -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);
 }
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars
  2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
                     ` (4 preceding siblings ...)
  2012-10-18 16:31   ` [PATCH 6/6] mmc: core: sdio_bus.c: Removed unnecessary NULL check routine Sangho Yi
@ 2012-10-18 16:47   ` Joe Perches
  5 siblings, 0 replies; 88+ messages in thread
From: Joe Perches @ 2012-10-18 16:47 UTC (permalink / raw)
  To: Sangho Yi
  Cc: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb, linux-kernel, linux-mmc

On Fri, 2012-10-19 at 01:31 +0900, Sangho Yi wrote:
> 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.

> diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
[]
> @@ -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.



^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars
  2012-10-18 16:31   ` [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars Sangho Yi
@ 2012-10-18 23:54     ` Jaehoon Chung
  0 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2012-10-18 23:54 UTC (permalink / raw)
  To: Sangho Yi
  Cc: paul.gortmaker, stefan.xk.nilsson, linus.walleij, ulf.hansson,
	rjw, girish.shivananjappa, cjb, linux-kernel, linux-mmc

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

On 10/19/2012 01:31 AM, Sangho Yi wrote:
> I fixed lines over 80 characters per line.
> 
> Signed-off-by: Sangho Yi <antiroot@gmail.com>
> ---
>  drivers/mmc/core/sdio_bus.c |    5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
> index 13146d2..efa30eb 100644
> --- a/drivers/mmc/core/sdio_bus.c
> +++ b/drivers/mmc/core/sdio_bus.c
> @@ -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);
>  
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
@ 2012-11-01 14:40 ` Konstantin Dorfman
  2012-11-05  6:20   ` Per Förlin
  2012-11-06  8:40   ` Jaehoon Chung
  0 siblings, 2 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-11-01 14:40 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, per.lkml, 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 <kdorfman@codeaurora.org>
---
 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(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..0e9bedb 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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;
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..7375476 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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 : "");
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d2a1eb4..f5885e9 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -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 *,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 06c42cf..a24d298 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 943550d..9681d8f 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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.
  */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..fc2d095 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-01 14:40 ` [PATCH v2] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
@ 2012-11-05  6:20   ` Per Förlin
  2012-11-05  7:15     ` Jaehoon Chung
                       ` (2 more replies)
  2012-11-06  8:40   ` Jaehoon Chung
  1 sibling, 3 replies; 88+ messages in thread
From: Per Förlin @ 2012-11-05  6:20 UTC (permalink / raw)
  To: Konstantin Dorfman; +Cc: cjb, linux-mmc, per.lkml

Hi Konstantin,

On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
> 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%.
What HW and what test cases have you been running?

> 
> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
> ---
Please add a change log here with a brief description of the changes since last version

>  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(-)
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..0e9bedb 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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;
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..7375476 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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 : "");
> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> index d2a1eb4..f5885e9 100644
> --- a/drivers/mmc/card/queue.h
> +++ b/drivers/mmc/card/queue.h
> @@ -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 *,
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 06c42cf..a24d298 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 943550d..9681d8f 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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.
>   */
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..fc2d095 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-05  6:20   ` Per Förlin
@ 2012-11-05  7:15     ` Jaehoon Chung
  2012-11-12 12:10       ` Konstantin Dorfman
  2012-11-08 10:41     ` Jaehoon Chung
  2012-11-12 12:49     ` Konstantin Dorfman
  2 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2012-11-05  7:15 UTC (permalink / raw)
  To: Per Förlin; +Cc: Konstantin Dorfman, cjb, linux-mmc, per.lkml

Hi Konstantin,
On 11/05/2012 03:20 PM, Per Förlin wrote:
> Hi Konstantin,
> 
> On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
>> 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%.
> 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
> 
>>
>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>> ---
> Please add a change log here with a brief description of the changes since last version
> 
>>  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(-)
>>
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..0e9bedb 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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;
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index fadf52e..7375476 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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 : "");
>> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
>> index d2a1eb4..f5885e9 100644
>> --- a/drivers/mmc/card/queue.h
>> +++ b/drivers/mmc/card/queue.h
>> @@ -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 *,
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 06c42cf..a24d298 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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);
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 943550d..9681d8f 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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.
>>   */
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..fc2d095 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-01 14:40 ` [PATCH v2] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
  2012-11-05  6:20   ` Per Förlin
@ 2012-11-06  8:40   ` Jaehoon Chung
  2012-11-12 12:42     ` Konstantin Dorfman
  1 sibling, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2012-11-06  8:40 UTC (permalink / raw)
  To: Konstantin Dorfman; +Cc: cjb, linux-mmc, per.lkml

Hi Konstantin,

On 11/01/2012 11:40 PM, Konstantin Dorfman wrote:
> 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 <kdorfman@codeaurora.org>
> ---
>  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(-)
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..0e9bedb 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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)
I think we can make a line..
> +				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;
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
>  
>  	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;
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..7375476 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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 : "");
> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> index d2a1eb4..f5885e9 100644
> --- a/drivers/mmc/card/queue.h
> +++ b/drivers/mmc/card/queue.h
> @@ -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 *,
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 06c42cf..a24d298 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 943550d..9681d8f 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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.
>   */
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..fc2d095 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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;
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
  2012-10-16  7:53   ` Jaehoon Chung
@ 2012-11-07  4:28       ` merez
  0 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-11-07  4:28 UTC (permalink / raw)
  Cc: Maya Erez, linux-mmc, linux-arm-msm, Jaehoon Chung, open list

Hi Jaehoon,

Any update on this patch review and testing?

Thanks,
Maya
On Mon, October 15, 2012 11:53 pm, Jaehoon Chung wrote:
> 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 Chung
>
>> ---
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..ed040d5 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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?
>> +
>>  	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.
>> +
>>  	do {
>>  		if (rqc) {
>>  			/*
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e96f5cf 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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);
> if mmc_stop_bkops is failed..?
>> +			}
>> +
>>  			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);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 6612163..fd8783d 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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);
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 7509de1..f8ff640 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -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;
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 78cc3be..0e4d55a 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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);
>>
>>
>
>


-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
@ 2012-11-07  4:28       ` merez
  0 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-11-07  4:28 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Maya Erez, linux-mmc, linux-arm-msm, Jaehoon Chung, open list

Hi Jaehoon,

Any update on this patch review and testing?

Thanks,
Maya
On Mon, October 15, 2012 11:53 pm, Jaehoon Chung wrote:
> 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 Chung
>
>> ---
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..ed040d5 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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?
>> +
>>  	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.
>> +
>>  	do {
>>  		if (rqc) {
>>  			/*
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e96f5cf 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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);
> if mmc_stop_bkops is failed..?
>> +			}
>> +
>>  			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);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 6612163..fd8783d 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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);
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 7509de1..f8ff640 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -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;
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 78cc3be..0e4d55a 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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);
>>
>>
>
>


-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
  2012-11-07  4:28       ` merez
  (?)
@ 2012-11-08  5:26       ` Jaehoon Chung
  -1 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2012-11-08  5:26 UTC (permalink / raw)
  To: merez; +Cc: Jaehoon Chung, linux-mmc, linux-arm-msm, open list

Hi Maya, 

I tested with dw-mmc controller.
It's look good to me.

Tested-by: Jaehoon Chung <jh80.chung@samsung.com>

On 11/07/2012 01:28 PM, merez@codeaurora.org wrote:
> Hi Jaehoon,
> 
> Any update on this patch review and testing?
> 
> Thanks,
> Maya
> On Mon, October 15, 2012 11:53 pm, Jaehoon Chung wrote:
>> 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 Chung
>>
>>> ---
>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>> index 172a768..ed040d5 100644
>>> --- a/drivers/mmc/card/block.c
>>> +++ b/drivers/mmc/card/block.c
>>> @@ -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?
>>> +
>>>  	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.
>>> +
>>>  	do {
>>>  		if (rqc) {
>>>  			/*
>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>> index e360a97..e96f5cf 100644
>>> --- a/drivers/mmc/card/queue.c
>>> +++ b/drivers/mmc/card/queue.c
>>> @@ -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);
>> if mmc_stop_bkops is failed..?
>>> +			}
>>> +
>>>  			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);
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 6612163..fd8783d 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -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);
>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>> index 7509de1..f8ff640 100644
>>> --- a/drivers/mmc/core/mmc.c
>>> +++ b/drivers/mmc/core/mmc.c
>>> @@ -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;
>>>
>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>> index 78cc3be..0e4d55a 100644
>>> --- a/include/linux/mmc/card.h
>>> +++ b/include/linux/mmc/card.h
>>> @@ -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;
>>>  };
>>>
>>>  /*
>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>> index 9b9cdaf..665d345 100644
>>> --- a/include/linux/mmc/core.h
>>> +++ b/include/linux/mmc/core.h
>>> @@ -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);
>>>
>>>
>>
>>
> 
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-05  6:20   ` Per Förlin
  2012-11-05  7:15     ` Jaehoon Chung
@ 2012-11-08 10:41     ` Jaehoon Chung
  2012-11-08 12:51       ` merez
  2012-11-12 12:49     ` Konstantin Dorfman
  2 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2012-11-08 10:41 UTC (permalink / raw)
  To: Per Förlin; +Cc: Konstantin Dorfman, cjb, linux-mmc, per.lkml

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 = eda3046c

What's wrong?

Best Regards,
Jaehoon Chung

On 11/05/2012 03:20 PM, Per Förlin wrote:
> Hi Konstantin,
> 
> On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
>> 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%.
> What HW and what test cases have you been running?
> 
>>
>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>> ---
> Please add a change log here with a brief description of the changes since last version
> 
>>  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(-)
>>
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..0e9bedb 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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;
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index fadf52e..7375476 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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 : "");
>> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
>> index d2a1eb4..f5885e9 100644
>> --- a/drivers/mmc/card/queue.h
>> +++ b/drivers/mmc/card/queue.h
>> @@ -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 *,
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 06c42cf..a24d298 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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);
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 943550d..9681d8f 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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.
>>   */
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..fc2d095 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-08 10:41     ` Jaehoon Chung
@ 2012-11-08 12:51       ` merez
  2012-11-09  5:46         ` Jaehoon Chung
  0 siblings, 1 reply; 88+ messages in thread
From: merez @ 2012-11-08 12:51 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Per Förlin, Konstantin Dorfman, cjb, linux-mmc, per.lkml

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
On Thu, November 8, 2012 2:41 am, Jaehoon Chung wrote:
> 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 = eda3046c
>
> What's wrong?
>
> Best Regards,
> Jaehoon Chung
>
> On 11/05/2012 03:20 PM, Per Förlin wrote:
>> Hi Konstantin,
>>
>> On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
>>> 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%.
>> What HW and what test cases have you been running?
>>
>>>
>>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>>> ---
>> Please add a change log here with a brief description of the changes
>> since last version
>>
>>>  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(-)
>>>
>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>> index 172a768..0e9bedb 100644
>>> --- a/drivers/mmc/card/block.c
>>> +++ b/drivers/mmc/card/block.c
>>> @@ -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;
>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>> index fadf52e..7375476 100644
>>> --- a/drivers/mmc/card/queue.c
>>> +++ b/drivers/mmc/card/queue.c
>>> @@ -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 : "");
>>> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
>>> index d2a1eb4..f5885e9 100644
>>> --- a/drivers/mmc/card/queue.h
>>> +++ b/drivers/mmc/card/queue.h
>>> @@ -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 *,
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 06c42cf..a24d298 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -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);
>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>> index 943550d..9681d8f 100644
>>> --- a/include/linux/mmc/card.h
>>> +++ b/include/linux/mmc/card.h
>>> @@ -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.
>>>   */
>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>> index 9b9cdaf..fc2d095 100644
>>> --- a/include/linux/mmc/core.h
>>> +++ b/include/linux/mmc/core.h
>>> @@ -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
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>> the body of a message to majordomo@vger.kernel.org
>> 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 majordomo@vger.kernel.org
> 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


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-08 12:51       ` merez
@ 2012-11-09  5:46         ` Jaehoon Chung
  0 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2012-11-09  5:46 UTC (permalink / raw)
  To: merez
  Cc: Jaehoon Chung, Per Förlin, Konstantin Dorfman, cjb,
	linux-mmc, per.lkml

Hi Maya,

Thank you for reply. I will check the patch v1 and share the result.

Best Regards,
Jaehoon Chung

On 11/08/2012 09:51 PM, merez@codeaurora.org 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,
> Maya
> On Thu, November 8, 2012 2:41 am, Jaehoon Chung wrote:
>> 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 = eda3046c
>>
>> What's wrong?
>>
>> Best Regards,
>> Jaehoon Chung
>>
>> On 11/05/2012 03:20 PM, Per Förlin wrote:
>>> Hi Konstantin,
>>>
>>> On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
>>>> 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%.
>>> What HW and what test cases have you been running?
>>>
>>>>
>>>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>>>> ---
>>> Please add a change log here with a brief description of the changes
>>> since last version
>>>
>>>>  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(-)
>>>>
>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>> index 172a768..0e9bedb 100644
>>>> --- a/drivers/mmc/card/block.c
>>>> +++ b/drivers/mmc/card/block.c
>>>> @@ -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;
>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>> index fadf52e..7375476 100644
>>>> --- a/drivers/mmc/card/queue.c
>>>> +++ b/drivers/mmc/card/queue.c
>>>> @@ -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 : "");
>>>> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
>>>> index d2a1eb4..f5885e9 100644
>>>> --- a/drivers/mmc/card/queue.h
>>>> +++ b/drivers/mmc/card/queue.h
>>>> @@ -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 *,
>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>> index 06c42cf..a24d298 100644
>>>> --- a/drivers/mmc/core/core.c
>>>> +++ b/drivers/mmc/core/core.c
>>>> @@ -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);
>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>> index 943550d..9681d8f 100644
>>>> --- a/include/linux/mmc/card.h
>>>> +++ b/include/linux/mmc/card.h
>>>> @@ -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.
>>>>   */
>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>> index 9b9cdaf..fc2d095 100644
>>>> --- a/include/linux/mmc/core.h
>>>> +++ b/include/linux/mmc/core.h
>>>> @@ -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
>>>>
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>>> the body of a message to majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
> 
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
  2012-10-04 22:28   ` Maya Erez
                     ` (2 preceding siblings ...)
  (?)
@ 2012-11-11  9:25   ` Subhash Jadavani
  -1 siblings, 0 replies; 88+ messages in thread
From: Subhash Jadavani @ 2012-11-11  9:25 UTC (permalink / raw)
  To: Maya Erez; +Cc: linux-mmc, linux-arm-msm, Jaehoon Chung, open list


Thanks Maya. Looks good to me.
Reviewed-by: subhashj@codeaurora.org

Regards,
Subhash

On 10/5/2012 3:58 AM, Maya Erez wrote:
> 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.
>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
> 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.
> ---
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..ed040d5 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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) {
>   			/*
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index e360a97..e96f5cf 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 6612163..fd8783d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7509de1..f8ff640 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>   
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 78cc3be..0e4d55a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>   };
>   
>   /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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);
>   


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-05  7:15     ` Jaehoon Chung
@ 2012-11-12 12:10       ` Konstantin Dorfman
  0 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-11-12 12:10 UTC (permalink / raw)
  To: Jaehoon Chung; +Cc: Per Förlin, cjb, linux-mmc, per.lkml

On 11/05/2012 09:15 AM, Jaehoon Chung wrote:
Hello,
> Hi Konstantin,
> On 11/05/2012 03:20 PM, Per Förlin wrote:
>> Hi Konstantin,
>>
>> On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
>>> 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%.
>> 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,
-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-06  8:40   ` Jaehoon Chung
@ 2012-11-12 12:42     ` Konstantin Dorfman
  0 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-11-12 12:42 UTC (permalink / raw)
  To: Jaehoon Chung; +Cc: cjb, linux-mmc, per.lkml

Hello Jaehoon,

On 11/06/2012 10:40 AM, Jaehoon Chung wrote:
> Hi Konstantin,
> 
...
>>  
>> @@ -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

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,

-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v2] mmc: fix async request mechanism for sequential read scenarios
  2012-11-05  6:20   ` Per Förlin
  2012-11-05  7:15     ` Jaehoon Chung
  2012-11-08 10:41     ` Jaehoon Chung
@ 2012-11-12 12:49     ` Konstantin Dorfman
  2 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-11-12 12:49 UTC (permalink / raw)
  To: Per Förlin; +Cc: cjb, linux-mmc, per.lkml

On 11/05/2012 08:20 AM, Per Förlin wrote:
> Hi Konstantin,
> 
> On 11/01/2012 03:40 PM, Konstantin Dorfman wrote:
>> 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%.
> 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,

-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
@ 2012-11-12 16:51 ` Konstantin Dorfman
  2012-11-13 13:42   ` Seungwon Jeon
  0 siblings, 1 reply; 88+ messages in thread
From: Konstantin Dorfman @ 2012-11-12 16:51 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, per.lkml, jh80.chung, 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 <kdorfman@codeaurora.org>
---
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

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..34d8bd9 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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;
 }
 
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..ef575ac 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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 : "");
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d2a1eb4..f5885e9 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -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 *,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 06c42cf..3374195 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 943550d..9681d8f 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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.
  */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..aa17096 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* RE: [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
  2012-11-12 16:51 ` [PATCH v3] " Konstantin Dorfman
@ 2012-11-13 13:42   ` Seungwon Jeon
  2012-11-15 10:23     ` Seungwon Jeon
  0 siblings, 1 reply; 88+ messages in thread
From: Seungwon Jeon @ 2012-11-13 13:42 UTC (permalink / raw)
  To: 'Konstantin Dorfman', cjb; +Cc: linux-mmc, per.lkml, jh80.chung

Hi Konstantin,

I'm looking into this patch.
Idea looks good as a whole.

On Tuesday, November 13, 2012, Konstantin Dorfman <kdorfman@codeaurora.org> wrote:
> 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 <kdorfman@codeaurora.org>
> ---
> 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
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..34d8bd9 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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);
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

>  		}
>  	} 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;
>  }
> 
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..ef575ac 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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 : "");
> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> index d2a1eb4..f5885e9 100644
> --- a/drivers/mmc/card/queue.h
> +++ b/drivers/mmc/card/queue.h
> @@ -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 *,
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 06c42cf..3374195 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 943550d..9681d8f 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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.
>   */
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..aa17096 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
  2012-11-13 13:42   ` Seungwon Jeon
@ 2012-11-15 10:23     ` Seungwon Jeon
  2012-11-20  2:05       ` Seungwon Jeon
  0 siblings, 1 reply; 88+ messages in thread
From: Seungwon Jeon @ 2012-11-15 10:23 UTC (permalink / raw)
  To: 'Seungwon Jeon', 'Konstantin Dorfman', cjb
  Cc: linux-mmc, per.lkml, jh80.chung

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 <tgih.jun@samsung.com>
> Hi Konstantin,
> 
> I'm looking into this patch.
> Idea looks good as a whole.
> 
> On Tuesday, November 13, 2012, Konstantin Dorfman <kdorfman@codeaurora.org> wrote:
> > 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 <kdorfman@codeaurora.org>
> > ---
> > 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
> >
> > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> > index 172a768..34d8bd9 100644
> > --- a/drivers/mmc/card/block.c
> > +++ b/drivers/mmc/card/block.c
> > @@ -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);
> 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
> 
> >  		}
> >  	} 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;
> >  }
> >
> > diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> > index fadf52e..ef575ac 100644
> > --- a/drivers/mmc/card/queue.c
> > +++ b/drivers/mmc/card/queue.c
> > @@ -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 : "");
> > diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> > index d2a1eb4..f5885e9 100644
> > --- a/drivers/mmc/card/queue.h
> > +++ b/drivers/mmc/card/queue.h
> > @@ -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 *,
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 06c42cf..3374195 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -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;
Need to check NULL of error.
if (error)

Thanks,
Seungwon Jeon
> > +			/*
> > +			 * 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);
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 943550d..9681d8f 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -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.
> >   */
> > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> > index 9b9cdaf..aa17096 100644
> > --- a/include/linux/mmc/core.h
> > +++ b/include/linux/mmc/core.h
> > @@ -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 majordomo@vger.kernel.org
> > 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH v3] mmc: fix async request mechanism for sequential read scenarios
  2012-11-15 10:23     ` Seungwon Jeon
@ 2012-11-20  2:05       ` Seungwon Jeon
  0 siblings, 0 replies; 88+ messages in thread
From: Seungwon Jeon @ 2012-11-20  2:05 UTC (permalink / raw)
  To: 'Seungwon Jeon', 'Konstantin Dorfman', cjb
  Cc: linux-mmc, per.lkml, jh80.chung

On Thursday, November 15, 2012, Seungwon Jeon <tgih.jun@samsung.com>
> 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?
Please let me correct my test. There is something applied wrong.
Anyway I saw the working fine. Sorry for the confusion.

Thanks,
Seungwon Jeon
> 
> On Tuesday, November 13, 2012, Seungwon Jeon <tgih.jun@samsung.com>
> > Hi Konstantin,
> >
> > I'm looking into this patch.
> > Idea looks good as a whole.
> >
> > On Tuesday, November 13, 2012, Konstantin Dorfman <kdorfman@codeaurora.org> wrote:
> > > 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 <kdorfman@codeaurora.org>
> > > ---
> > > 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
> > >
> > > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> > > index 172a768..34d8bd9 100644
> > > --- a/drivers/mmc/card/block.c
> > > +++ b/drivers/mmc/card/block.c
> > > @@ -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);
> > 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
> >
> > >  		}
> > >  	} 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;
> > >  }
> > >
> > > diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> > > index fadf52e..ef575ac 100644
> > > --- a/drivers/mmc/card/queue.c
> > > +++ b/drivers/mmc/card/queue.c
> > > @@ -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 : "");
> > > diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> > > index d2a1eb4..f5885e9 100644
> > > --- a/drivers/mmc/card/queue.h
> > > +++ b/drivers/mmc/card/queue.h
> > > @@ -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 *,
> > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > > index 06c42cf..3374195 100644
> > > --- a/drivers/mmc/core/core.c
> > > +++ b/drivers/mmc/core/core.c
> > > @@ -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;
> Need to check NULL of error.
> if (error)
> 
> Thanks,
> Seungwon Jeon
> > > +			/*
> > > +			 * 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);
> > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > index 943550d..9681d8f 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -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.
> > >   */
> > > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> > > index 9b9cdaf..aa17096 100644
> > > --- a/include/linux/mmc/core.h
> > > +++ b/include/linux/mmc/core.h
> > > @@ -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 majordomo@vger.kernel.org
> > > 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 majordomo@vger.kernel.org
> > 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v3] mmc: core: Add support for idle time BKOPS
@ 2012-11-25 11:56   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-11-25 11:56 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-arm-msm, Maya Erez, open list

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.

Signed-off-by: Maya Erez <merez@codeaurora.org>

---
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(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..40b4ae3 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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) {
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..9d0c96a 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 06c42cf..72ae15b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 7cc4638..dba76e3 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -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;
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 943550d..224e2a5 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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;
 };
 
 /*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..665d345 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH v3] mmc: core: Add support for idle time BKOPS
@ 2012-11-25 11:56   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-11-25 11:56 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-arm-msm, Maya Erez, open list

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.

Signed-off-by: Maya Erez <merez@codeaurora.org>

---
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(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..40b4ae3 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -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) {
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..9d0c96a 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 06c42cf..72ae15b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 7cc4638..dba76e3 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -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;
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 943550d..224e2a5 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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;
 };
 
 /*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9b9cdaf..665d345 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-11-25 11:56   ` Maya Erez
@ 2012-11-25 12:46     ` merez
  -1 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-11-25 12:46 UTC (permalink / raw)
  Cc: linux-mmc, linux-arm-msm, Maya Erez, open list

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
On Sun, November 25, 2012 3:56 am, Maya Erez wrote:
> 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.
>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
>
> ---
> 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(-)
>
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..40b4ae3 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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) {
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..9d0c96a 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 06c42cf..72ae15b 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7cc4638..dba76e3 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 943550d..224e2a5 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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 majordomo@vger.kernel.org
> 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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
@ 2012-11-25 12:46     ` merez
  0 siblings, 0 replies; 88+ messages in thread
From: merez @ 2012-11-25 12:46 UTC (permalink / raw)
  To: Maya Erez; +Cc: linux-mmc, linux-arm-msm, Maya Erez, open list

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
On Sun, November 25, 2012 3:56 am, Maya Erez wrote:
> 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.
>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
>
> ---
> 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(-)
>
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..40b4ae3 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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) {
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..9d0c96a 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 06c42cf..72ae15b 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7cc4638..dba76e3 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 943550d..224e2a5 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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 majordomo@vger.kernel.org
> 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


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-11-25 12:46     ` merez
  (?)
@ 2012-11-28 14:22     ` Chris Ball
  -1 siblings, 0 replies; 88+ messages in thread
From: Chris Ball @ 2012-11-28 14:22 UTC (permalink / raw)
  To: merez; +Cc: linux-mmc, linux-arm-msm, open list

Hi Maya,

On Sun, Nov 25 2012, merez@codeaurora.org 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_host and remove_host
> operations.
> 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.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-11-25 11:56   ` Maya Erez
  (?)
  (?)
@ 2012-11-29 12:40   ` Jaehoon Chung
  2012-12-03  9:49     ` merez
  -1 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2012-11-29 12:40 UTC (permalink / raw)
  To: Maya Erez; +Cc: linux-mmc, linux-arm-msm, open list

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.

On 11/25/2012 08:56 PM, Maya Erez wrote:
> 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.
> 
> Signed-off-by: Maya Erez <merez@codeaurora.org>
> 
> ---
> 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(-)
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 172a768..40b4ae3 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -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);
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

> +		}
> +	}
>  
>  	ret = mmc_blk_part_switch(card, md);
>  	if (ret) {
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..9d0c96a 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 06c42cf..72ae15b 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 7cc4638..dba76e3 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -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;
>  
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 943550d..224e2a5 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -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;
>  };
>  
>  /*
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 9b9cdaf..665d345 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -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);
>  
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-11-29 12:40   ` Jaehoon Chung
@ 2012-12-03  9:49     ` merez
  2012-12-04  9:52       ` Ulf Hansson
  0 siblings, 1 reply; 88+ messages in thread
From: merez @ 2012-12-03  9:49 UTC (permalink / raw)
  To: Jaehoon Chung; +Cc: Maya Erez, linux-mmc, linux-arm-msm, open list

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
On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
> 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.
>
> On 11/25/2012 08:56 PM, Maya Erez wrote:
>> 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.
>>
>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>>
>> ---
>> 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(-)
>>
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..40b4ae3 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -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);
> 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
>
>> +		}
>> +	}
>>
>>  	ret = mmc_blk_part_switch(card, md);
>>  	if (ret) {
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index fadf52e..9d0c96a 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -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);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 06c42cf..72ae15b 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -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);
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 7cc4638..dba76e3 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -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;
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 943550d..224e2a5 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -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;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -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);
>>
>>
>
>


-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-03  9:49     ` merez
@ 2012-12-04  9:52       ` Ulf Hansson
  2012-12-04 21:17         ` merez
  0 siblings, 1 reply; 88+ messages in thread
From: Ulf Hansson @ 2012-12-04  9:52 UTC (permalink / raw)
  To: merez; +Cc: Jaehoon Chung, linux-mmc, linux-arm-msm, open list

On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>> 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.
>>
>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>> 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.

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.

>>> 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.

>>> 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 <merez@codeaurora.org>
>>>
>>> ---
>>> 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(-)
>>>
>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>> index 172a768..40b4ae3 100644
>>> --- a/drivers/mmc/card/block.c
>>> +++ b/drivers/mmc/card/block.c
>>> @@ -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);
>> 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
>>
>>> +            }
>>> +    }
>>>
>>>      ret = mmc_blk_part_switch(card, md);
>>>      if (ret) {
>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>> index fadf52e..9d0c96a 100644
>>> --- a/drivers/mmc/card/queue.c
>>> +++ b/drivers/mmc/card/queue.c
>>> @@ -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);
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 06c42cf..72ae15b 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -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.

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.

>>> + * 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);

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?

>>>                      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);
>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>> index 7cc4638..dba76e3 100644
>>> --- a/drivers/mmc/core/mmc.c
>>> +++ b/drivers/mmc/core/mmc.c
>>> @@ -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);

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.

>>> +
>>> +                    /*
>>> +                     * 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;
>>>
>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>> index 943550d..224e2a5 100644
>>> --- a/include/linux/mmc/card.h
>>> +++ b/include/linux/mmc/card.h
>>> @@ -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;

As stated earlier, what is this timeout you are referring to? What suspend?

>>> +    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;

Could not find "sectors_changed" being used. Maybe you forgot to
remove this from previous version of the patch.

>>> +};
>>> +
>>>  /*
>>>   * 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;
>>>  };
>>>
>>>  /*
>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>> index 9b9cdaf..665d345 100644
>>> --- a/include/linux/mmc/core.h
>>> +++ b/include/linux/mmc/core.h
>>> @@ -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);
>>>
>>>
>>
>>
>
>
> --
> 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-kernel" in
> the body of a message to majordomo@vger.kernel.org
> 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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-04  9:52       ` Ulf Hansson
@ 2012-12-04 21:17         ` merez
  2012-12-06 10:18           ` Ulf Hansson
  0 siblings, 1 reply; 88+ messages in thread
From: merez @ 2012-12-04 21:17 UTC (permalink / raw)
  To: Ulf Hansson; +Cc: merez, Jaehoon Chung, linux-mmc, linux-arm-msm, open list

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

On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>> 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.
>>>
>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>> 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.
>
> 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.
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.
>
>>>> 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.
>
>>>> 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 <merez@codeaurora.org>
>>>>
>>>> ---
>>>> 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(-)
>>>>
>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>> index 172a768..40b4ae3 100644
>>>> --- a/drivers/mmc/card/block.c
>>>> +++ b/drivers/mmc/card/block.c
>>>> @@ -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);
>>> 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
>>>
>>>> +            }
>>>> +    }
>>>>
>>>>      ret = mmc_blk_part_switch(card, md);
>>>>      if (ret) {
>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>> index fadf52e..9d0c96a 100644
>>>> --- a/drivers/mmc/card/queue.c
>>>> +++ b/drivers/mmc/card/queue.c
>>>> @@ -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);
>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>> index 06c42cf..72ae15b 100644
>>>> --- a/drivers/mmc/core/core.c
>>>> +++ b/drivers/mmc/core/core.c
>>>> @@ -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.
>
> 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.
>
>>>> + * 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);
>
> 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?
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.
>
>>>>                      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);
>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>> index 7cc4638..dba76e3 100644
>>>> --- a/drivers/mmc/core/mmc.c
>>>> +++ b/drivers/mmc/core/mmc.c
>>>> @@ -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);
>
> 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.
>
>>>> +
>>>> +                    /*
>>>> +                     * 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;
>>>>
>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>> index 943550d..224e2a5 100644
>>>> --- a/include/linux/mmc/card.h
>>>> +++ b/include/linux/mmc/card.h
>>>> @@ -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;
>
> As stated earlier, what is this timeout you are referring to? What
> suspend?
Explained above.
>
>>>> +    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;
>
> Could not find "sectors_changed" being used. Maybe you forgot to
> remove this from previous version of the patch.
Yes, this should be removed.
>
>>>> +};
>>>> +
>>>>  /*
>>>>   * 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;
>>>>  };
>>>>
>>>>  /*
>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>> index 9b9cdaf..665d345 100644
>>>> --- a/include/linux/mmc/core.h
>>>> +++ b/include/linux/mmc/core.h
>>>> @@ -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);
>>>>
>>>>
>>>
>>>
>>
>>
>> --
>> 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-kernel"
>> in
>> the body of a message to majordomo@vger.kernel.org
>> 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 majordomo@vger.kernel.org
> 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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v4] mmc: fix async request mechanism for sequential read scenarios
@ 2012-12-05 13:38 ` Konstantin Dorfman
  2012-12-06  5:24   ` Seungwon Jeon
  0 siblings, 1 reply; 88+ messages in thread
From: Konstantin Dorfman @ 2012-12-05 13:38 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, per.lkml, 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 <kdorfman@codeaurora.org>
---
v4:     keeps new synchronization mechanism within mmc/core
	layer, so mmc/core external API's are not changed.
	- context_info moved into mmc_host struct
	- context_info initialized in mmc_attach_mmc()

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

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 21056b9..f4f295e 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -113,17 +113,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");
 
@@ -1363,9 +1352,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;
@@ -1374,6 +1366,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:
 			/*
@@ -1486,6 +1480,7 @@ 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)
@@ -1505,7 +1500,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;
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..0c37b49 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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->card->host->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,8 @@ static void mmc_request_fn(struct request_queue *q)
 {
 	struct mmc_queue *mq = q->queuedata;
 	struct request *req;
+	unsigned long flags;
+	struct mmc_context_info *cntx;
 
 	if (!mq) {
 		while ((req = blk_fetch_request(q)) != NULL) {
@@ -112,7 +122,20 @@ static void mmc_request_fn(struct request_queue *q)
 		return;
 	}
 
-	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
+	cntx = &mq->card->host->context_info;
+	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(&cntx->lock, flags);
+		if (cntx->is_waiting_last_req) {
+			cntx->is_new_req = true;
+			wake_up_interruptible(&cntx->wait);
+		}
+		spin_unlock_irqrestore(&cntx->lock, flags);
+	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
 		wake_up_process(mq->thread);
 }
 
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d2a1eb4..970d9e7 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -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;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index aaed768..855336d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -319,11 +319,44 @@ 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->host->context_info.is_done_rcv = true;
+	wake_up_interruptible(&mrq->host->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;
+	mrq->host = host;
+	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);
@@ -337,6 +370,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 = &host->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)
 {
@@ -426,8 +513,21 @@ 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);
+			}
+			if (error)
+				*error = err;
+			/*
+			 * The previous request was not completed,
+			 * nothing to return
+			 */
+			return NULL;
+		}
 		/*
 		 * Check BKOPS urgency for each R1 response
 		 */
@@ -439,7 +539,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);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index e6e3911..dd3bc11 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1497,6 +1497,12 @@ int mmc_attach_mmc(struct mmc_host *host)
 	BUG_ON(!host);
 	WARN_ON(!host->claimed);
 
+	spin_lock_init(&host->context_info.lock);
+	host->context_info.is_new_req = false;
+	host->context_info.is_done_rcv = false;
+	host->context_info.is_waiting_last_req = false;
+	init_waitqueue_head(&host->context_info.wait);
+
 	/* Set correct bus mode for MMC before attempting attach */
 	if (!mmc_host_is_spi(host))
 		mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 5c69315..be2500a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -187,6 +187,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.
  */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 5bf7c22..724cc95 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -120,6 +120,7 @@ struct mmc_data {
 	s32			host_cookie;	/* host private data */
 };
 
+struct mmc_host;
 struct mmc_request {
 	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
 	struct mmc_command	*cmd;
@@ -128,9 +129,9 @@ struct mmc_request {
 
 	struct completion	completion;
 	void			(*done)(struct mmc_request *);/* completion function */
+	struct mmc_host         *host;
 };
 
-struct mmc_host;
 struct mmc_card;
 struct mmc_async_req;
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 23df21e..08a8203 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -176,6 +176,21 @@ struct mmc_supply {
 	struct regulator *vqmmc;	/* Optional Vccq supply */
 };
 
+/**
+ * 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_host {
 	struct device		*parent;
 	struct device		class_dev;
@@ -330,6 +345,7 @@ struct mmc_host {
 	struct dentry		*debugfs_root;
 
 	struct mmc_async_req	*areq;		/* active async req */
+	struct mmc_context_info context_info;   /* async synchronization info */
 
 #ifdef CONFIG_FAIL_MMC_REQUEST
 	struct fault_attr	fail_mmc_request;
-- 
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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* RE: [PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-05 13:38 ` [PATCH v4] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
@ 2012-12-06  5:24   ` Seungwon Jeon
  2012-12-06 14:23     ` Konstantin Dorfman
  0 siblings, 1 reply; 88+ messages in thread
From: Seungwon Jeon @ 2012-12-06  5:24 UTC (permalink / raw)
  To: 'Konstantin Dorfman', cjb; +Cc: linux-mmc, per.lkml

Hi,

Doesn't it consider SD path?
It have a problem with uninitialized 'context_info'.
'context_info' is moved into mmc_host struct in v4.
Please, could you explain the reason in brief?

Thanks,
Seungwon Jeon

On Wednesday, December 05, 2012, Konstantin Dorfman wrote:
> 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 <kdorfman@codeaurora.org>
> ---
> v4:     keeps new synchronization mechanism within mmc/core
> 	layer, so mmc/core external API's are not changed.
> 	- context_info moved into mmc_host struct
> 	- context_info initialized in mmc_attach_mmc()
> 
> 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
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 21056b9..f4f295e 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -113,17 +113,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");
> 
> @@ -1363,9 +1352,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;
> @@ -1374,6 +1366,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:
>  			/*
> @@ -1486,6 +1480,7 @@ 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)
> @@ -1505,7 +1500,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;
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..0c37b49 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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->card->host->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,8 @@ static void mmc_request_fn(struct request_queue *q)
>  {
>  	struct mmc_queue *mq = q->queuedata;
>  	struct request *req;
> +	unsigned long flags;
> +	struct mmc_context_info *cntx;
> 
>  	if (!mq) {
>  		while ((req = blk_fetch_request(q)) != NULL) {
> @@ -112,7 +122,20 @@ static void mmc_request_fn(struct request_queue *q)
>  		return;
>  	}
> 
> -	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
> +	cntx = &mq->card->host->context_info;
> +	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(&cntx->lock, flags);
> +		if (cntx->is_waiting_last_req) {
> +			cntx->is_new_req = true;
> +			wake_up_interruptible(&cntx->wait);
> +		}
> +		spin_unlock_irqrestore(&cntx->lock, flags);
> +	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
>  		wake_up_process(mq->thread);
>  }
> 
> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> index d2a1eb4..970d9e7 100644
> --- a/drivers/mmc/card/queue.h
> +++ b/drivers/mmc/card/queue.h
> @@ -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;
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index aaed768..855336d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -319,11 +319,44 @@ 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->host->context_info.is_done_rcv = true;
> +	wake_up_interruptible(&mrq->host->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;
> +	mrq->host = host;
> +	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);
> @@ -337,6 +370,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 = &host->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)
>  {
> @@ -426,8 +513,21 @@ 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);
> +			}
> +			if (error)
> +				*error = err;
> +			/*
> +			 * The previous request was not completed,
> +			 * nothing to return
> +			 */
> +			return NULL;
> +		}
>  		/*
>  		 * Check BKOPS urgency for each R1 response
>  		 */
> @@ -439,7 +539,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);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index e6e3911..dd3bc11 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -1497,6 +1497,12 @@ int mmc_attach_mmc(struct mmc_host *host)
>  	BUG_ON(!host);
>  	WARN_ON(!host->claimed);
> 
> +	spin_lock_init(&host->context_info.lock);
> +	host->context_info.is_new_req = false;
> +	host->context_info.is_done_rcv = false;
> +	host->context_info.is_waiting_last_req = false;
> +	init_waitqueue_head(&host->context_info.wait);
> +
>  	/* Set correct bus mode for MMC before attempting attach */
>  	if (!mmc_host_is_spi(host))
>  		mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 5c69315..be2500a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -187,6 +187,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.
>   */
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 5bf7c22..724cc95 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -120,6 +120,7 @@ struct mmc_data {
>  	s32			host_cookie;	/* host private data */
>  };
> 
> +struct mmc_host;
>  struct mmc_request {
>  	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
>  	struct mmc_command	*cmd;
> @@ -128,9 +129,9 @@ struct mmc_request {
> 
>  	struct completion	completion;
>  	void			(*done)(struct mmc_request *);/* completion function */
> +	struct mmc_host         *host;
>  };
> 
> -struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
> 
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 23df21e..08a8203 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -176,6 +176,21 @@ struct mmc_supply {
>  	struct regulator *vqmmc;	/* Optional Vccq supply */
>  };
> 
> +/**
> + * 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_host {
>  	struct device		*parent;
>  	struct device		class_dev;
> @@ -330,6 +345,7 @@ struct mmc_host {
>  	struct dentry		*debugfs_root;
> 
>  	struct mmc_async_req	*areq;		/* active async req */
> +	struct mmc_context_info context_info;   /* async synchronization info */
> 
>  #ifdef CONFIG_FAIL_MMC_REQUEST
>  	struct fault_attr	fail_mmc_request;
> --
> 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-04 21:17         ` merez
@ 2012-12-06 10:18           ` Ulf Hansson
  2012-12-12 12:32             ` merez
  0 siblings, 1 reply; 88+ messages in thread
From: Ulf Hansson @ 2012-12-06 10:18 UTC (permalink / raw)
  To: merez; +Cc: Jaehoon Chung, linux-mmc, linux-arm-msm, open list

Hi Maya,

On 4 December 2012 22:17,  <merez@codeaurora.org> 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.

> 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 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.

>
> 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.

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.

> 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
>
> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>> 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.
>>>>
>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>> 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.
>>
>> 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.
> 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.

>>
>>>>> 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.
>>
>>>>> 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 <merez@codeaurora.org>
>>>>>
>>>>> ---
>>>>> 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(-)
>>>>>
>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>>> index 172a768..40b4ae3 100644
>>>>> --- a/drivers/mmc/card/block.c
>>>>> +++ b/drivers/mmc/card/block.c
>>>>> @@ -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);
>>>> 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
>>>>
>>>>> +            }
>>>>> +    }
>>>>>
>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>      if (ret) {
>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>>> index fadf52e..9d0c96a 100644
>>>>> --- a/drivers/mmc/card/queue.c
>>>>> +++ b/drivers/mmc/card/queue.c
>>>>> @@ -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);
>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>> index 06c42cf..72ae15b 100644
>>>>> --- a/drivers/mmc/core/core.c
>>>>> +++ b/drivers/mmc/core/core.c
>>>>> @@ -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.
>>
>> 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.
>>
>>>>> + * 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);
>>
>> 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?
> 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.
>>
>>>>>                      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);
>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>> index 7cc4638..dba76e3 100644
>>>>> --- a/drivers/mmc/core/mmc.c
>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>> @@ -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);
>>
>> 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.
>>
>>>>> +
>>>>> +                    /*
>>>>> +                     * 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;
>>>>>
>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>> index 943550d..224e2a5 100644
>>>>> --- a/include/linux/mmc/card.h
>>>>> +++ b/include/linux/mmc/card.h
>>>>> @@ -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;
>>
>> As stated earlier, what is this timeout you are referring to? What
>> suspend?
> Explained above.
>>
>>>>> +    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;
>>
>> Could not find "sectors_changed" being used. Maybe you forgot to
>> remove this from previous version of the patch.
> Yes, this should be removed.
>>
>>>>> +};
>>>>> +
>>>>>  /*
>>>>>   * 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;
>>>>>  };
>>>>>
>>>>>  /*
>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>> index 9b9cdaf..665d345 100644
>>>>> --- a/include/linux/mmc/core.h
>>>>> +++ b/include/linux/mmc/core.h
>>>>> @@ -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);
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> 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-kernel"
>>> in
>>> the body of a message to majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> 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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-06  5:24   ` Seungwon Jeon
@ 2012-12-06 14:23     ` Konstantin Dorfman
  0 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-12-06 14:23 UTC (permalink / raw)
  To: Seungwon Jeon; +Cc: cjb, linux-mmc, per.lkml

On 12/06/2012 07:24 AM, Seungwon Jeon wrote:
> Hi,
> 
> Doesn't it consider SD path?
> It have a problem with uninitialized 'context_info'.
> 'context_info' is moved into mmc_host struct in v4.
> Please, could you explain the reason in brief?
> 

The problem I'm trying to resolve with the v4 change is: there should be
no core/core.c API change from upper layers point of view.

There was such violation, when the synchronization mechanism of async
request for data commands was changed (from "completion" to "wait_event"
+ number of flags to pass event reason).

Before v4, the 'context_info' initialized by mmc_init_queue() function
and this is valid only for mmcblk. For example when running the mmc_test
module, the code for card/queue.c, card/block.c layers is not run at all
and the struct remains uninitialized.

In the current version the initialization was moved into
mmc_attach_mmc(), but now, I understand, that this initialization should
be moved even before mmc_attach_XXX() (XXX is sdio/sd/mmc...) - it may
be placed in  mmc_rescan_try_freq().

Will resend v4 after the testing.

'mmc_host' structure contains a host information for all sdio/sd/mmc, so
'context_info' may be accessed by any protocol willing to use
NEW_REQUEST notification (when async request mechanism is used).

-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios
@ 2012-12-10 14:23 ` Konstantin Dorfman
  2012-12-12  9:26   ` Seungwon Jeon
  2012-12-17 12:26   ` Seungwon Jeon
  0 siblings, 2 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-12-10 14:23 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, per.lkml, tgih.jun, 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 <kdorfman@codeaurora.org>
---


v4:     
	keeps new synchronization mechanism within mmc/core
	layer, so mmc/core external API's are not changed.
	- context_info moved into mmc_host struct
	- context_info initialized in mmc_rescan_try_freq()

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 |   25 +++++------
 drivers/mmc/card/queue.c |   27 ++++++++++-
 drivers/mmc/card/queue.h |    2 +
 drivers/mmc/core/core.c  |  112 ++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/mmc/card.h |   12 +++++
 include/linux/mmc/core.h |    3 +-
 include/linux/mmc/host.h |   16 +++++++
 7 files changed, 177 insertions(+), 20 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 21056b9..6fe0412 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -113,17 +113,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");
 
@@ -1180,6 +1169,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
 	memset(brq, 0, sizeof(struct mmc_blk_request));
 	brq->mrq.cmd = &brq->cmd;
 	brq->mrq.data = &brq->data;
+	brq->mrq.host = card->host;
 
 	brq->cmd.arg = blk_rq_pos(req);
 	if (!mmc_card_blockaddr(card))
@@ -1363,9 +1353,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;
@@ -1374,6 +1367,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:
 			/*
@@ -1486,6 +1481,7 @@ 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)
@@ -1505,9 +1501,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;
 }
 
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..0c37b49 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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->card->host->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,8 @@ static void mmc_request_fn(struct request_queue *q)
 {
 	struct mmc_queue *mq = q->queuedata;
 	struct request *req;
+	unsigned long flags;
+	struct mmc_context_info *cntx;
 
 	if (!mq) {
 		while ((req = blk_fetch_request(q)) != NULL) {
@@ -112,7 +122,20 @@ static void mmc_request_fn(struct request_queue *q)
 		return;
 	}
 
-	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
+	cntx = &mq->card->host->context_info;
+	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(&cntx->lock, flags);
+		if (cntx->is_waiting_last_req) {
+			cntx->is_new_req = true;
+			wake_up_interruptible(&cntx->wait);
+		}
+		spin_unlock_irqrestore(&cntx->lock, flags);
+	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
 		wake_up_process(mq->thread);
 }
 
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d2a1eb4..970d9e7 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -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;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index aaed768..344cd3a 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -319,11 +319,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->host->context_info.is_done_rcv = true;
+	wake_up_interruptible(&mrq->host->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);
@@ -337,6 +369,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 = &host->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)
 {
@@ -426,8 +512,21 @@ 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);
+			}
+			if (error)
+				*error = err;
+			/*
+			 * The previous request was not completed,
+			 * nothing to return
+			 */
+			return NULL;
+		}
 		/*
 		 * Check BKOPS urgency for each R1 response
 		 */
@@ -439,7 +538,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);
@@ -455,6 +554,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
 
 	if (error)
 		*error = err;
+
 	return data;
 }
 EXPORT_SYMBOL(mmc_start_req);
@@ -2086,6 +2186,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 
 	mmc_send_if_cond(host, host->ocr_avail);
 
+	spin_lock_init(&host->context_info.lock);
+	host->context_info.is_new_req = false;
+	host->context_info.is_done_rcv = false;
+	host->context_info.is_waiting_last_req = false;
+	init_waitqueue_head(&host->context_info.wait);
+
 	/* Order's important: probe SDIO, then SD, then MMC */
 	if (!mmc_attach_sdio(host))
 		return 0;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 5c69315..be2500a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -187,6 +187,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.
  */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 5bf7c22..724cc95 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -120,6 +120,7 @@ struct mmc_data {
 	s32			host_cookie;	/* host private data */
 };
 
+struct mmc_host;
 struct mmc_request {
 	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
 	struct mmc_command	*cmd;
@@ -128,9 +129,9 @@ struct mmc_request {
 
 	struct completion	completion;
 	void			(*done)(struct mmc_request *);/* completion function */
+	struct mmc_host         *host;
 };
 
-struct mmc_host;
 struct mmc_card;
 struct mmc_async_req;
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 61a10c1..c26c180 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -177,6 +177,21 @@ struct mmc_supply {
 	struct regulator *vqmmc;	/* Optional Vccq supply */
 };
 
+/**
+ * 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_host {
 	struct device		*parent;
 	struct device		class_dev;
@@ -331,6 +346,7 @@ struct mmc_host {
 	struct dentry		*debugfs_root;
 
 	struct mmc_async_req	*areq;		/* active async req */
+	struct mmc_context_info context_info;   /* async synchronization info */
 
 #ifdef CONFIG_FAIL_MMC_REQUEST
 	struct fault_attr	fail_mmc_request;
-- 
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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* RE: [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-10 14:23 ` [RESEND PATCH " Konstantin Dorfman
@ 2012-12-12  9:26   ` Seungwon Jeon
  2012-12-17 12:26   ` Seungwon Jeon
  1 sibling, 0 replies; 88+ messages in thread
From: Seungwon Jeon @ 2012-12-12  9:26 UTC (permalink / raw)
  To: 'Konstantin Dorfman', cjb; +Cc: linux-mmc, per.lkml

On Monday, December 10, 2012, Konstantin Dorfman wrote:
> 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 <kdorfman@codeaurora.org>
> ---
> 
> 
> v4:
> 	keeps new synchronization mechanism within mmc/core
> 	layer, so mmc/core external API's are not changed.
> 	- context_info moved into mmc_host struct
> 	- context_info initialized in mmc_rescan_try_freq()
> 
> 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 |   25 +++++------
>  drivers/mmc/card/queue.c |   27 ++++++++++-
>  drivers/mmc/card/queue.h |    2 +
>  drivers/mmc/core/core.c  |  112 ++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/mmc/card.h |   12 +++++
>  include/linux/mmc/core.h |    3 +-
>  include/linux/mmc/host.h |   16 +++++++
>  7 files changed, 177 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 21056b9..6fe0412 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -113,17 +113,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");
> 
> @@ -1180,6 +1169,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
>  	memset(brq, 0, sizeof(struct mmc_blk_request));
>  	brq->mrq.cmd = &brq->cmd;
>  	brq->mrq.data = &brq->data;
> +	brq->mrq.host = card->host;
Above setting have been added at __mmc_start_data_req function in previous sending.
Currently there is no setting for sdio. Considering sdio case, the former would be better.

> 
>  	brq->cmd.arg = blk_rq_pos(req);
>  	if (!mmc_card_blockaddr(card))
> @@ -1363,9 +1353,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;
> @@ -1374,6 +1367,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:
>  			/*
> @@ -1486,6 +1481,7 @@ 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)
> @@ -1505,9 +1501,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;
>  }
> 
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..0c37b49 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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->card->host->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,8 @@ static void mmc_request_fn(struct request_queue *q)
>  {
>  	struct mmc_queue *mq = q->queuedata;
>  	struct request *req;
> +	unsigned long flags;
> +	struct mmc_context_info *cntx;
> 
>  	if (!mq) {
>  		while ((req = blk_fetch_request(q)) != NULL) {
> @@ -112,7 +122,20 @@ static void mmc_request_fn(struct request_queue *q)
>  		return;
>  	}
> 
> -	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
> +	cntx = &mq->card->host->context_info;
> +	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(&cntx->lock, flags);
> +		if (cntx->is_waiting_last_req) {
> +			cntx->is_new_req = true;
> +			wake_up_interruptible(&cntx->wait);
> +		}
> +		spin_unlock_irqrestore(&cntx->lock, flags);
> +	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
>  		wake_up_process(mq->thread);
>  }
> 
> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> index d2a1eb4..970d9e7 100644
> --- a/drivers/mmc/card/queue.h
> +++ b/drivers/mmc/card/queue.h
> @@ -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;
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index aaed768..344cd3a 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -319,11 +319,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->host->context_info.is_done_rcv = true;
> +	wake_up_interruptible(&mrq->host->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);
> @@ -337,6 +369,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 = &host->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)
>  {
> @@ -426,8 +512,21 @@ 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);
> +			}
> +			if (error)
> +				*error = err;
> +			/*
> +			 * The previous request was not completed,
> +			 * nothing to return
> +			 */
> +			return NULL;
> +		}
>  		/*
>  		 * Check BKOPS urgency for each R1 response
>  		 */
> @@ -439,7 +538,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);
> @@ -455,6 +554,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
> 
>  	if (error)
>  		*error = err;
> +
>  	return data;
>  }
>  EXPORT_SYMBOL(mmc_start_req);
> @@ -2086,6 +2186,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
> 
>  	mmc_send_if_cond(host, host->ocr_avail);
> 
> +	spin_lock_init(&host->context_info.lock);
> +	host->context_info.is_new_req = false;
> +	host->context_info.is_done_rcv = false;
> +	host->context_info.is_waiting_last_req = false;
> +	init_waitqueue_head(&host->context_info.wait);
> +
This path may be retired when mmc_rescan_try_freq is failed.
How about putting these in mmc_add_card(mmc/core/bus.c)
<Quote>
        ret = device_add(&card->dev);
        if (ret)
                return ret;
-> Here seems good point.

	mmc_card_set_present(card);
</Quote>
Thanks,
Seungwon Jeon

>  	/* Order's important: probe SDIO, then SD, then MMC */
>  	if (!mmc_attach_sdio(host))
>  		return 0;
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 5c69315..be2500a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -187,6 +187,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.
>   */
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 5bf7c22..724cc95 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -120,6 +120,7 @@ struct mmc_data {
>  	s32			host_cookie;	/* host private data */
>  };
> 
> +struct mmc_host;
>  struct mmc_request {
>  	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
>  	struct mmc_command	*cmd;
> @@ -128,9 +129,9 @@ struct mmc_request {
> 
>  	struct completion	completion;
>  	void			(*done)(struct mmc_request *);/* completion function */
> +	struct mmc_host         *host;
>  };
> 
> -struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
> 
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 61a10c1..c26c180 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -177,6 +177,21 @@ struct mmc_supply {
>  	struct regulator *vqmmc;	/* Optional Vccq supply */
>  };
> 
> +/**
> + * 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_host {
>  	struct device		*parent;
>  	struct device		class_dev;
> @@ -331,6 +346,7 @@ struct mmc_host {
>  	struct dentry		*debugfs_root;
> 
>  	struct mmc_async_req	*areq;		/* active async req */
> +	struct mmc_context_info context_info;   /* async synchronization info */
> 
>  #ifdef CONFIG_FAIL_MMC_REQUEST
>  	struct fault_attr	fail_mmc_request;
> --
> 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-06 10:18           ` Ulf Hansson
@ 2012-12-12 12:32             ` merez
  2012-12-13 10:17               ` Ulf Hansson
  0 siblings, 1 reply; 88+ messages in thread
From: merez @ 2012-12-12 12:32 UTC (permalink / raw)
  To: Ulf Hansson; +Cc: merez, Jaehoon Chung, linux-mmc, linux-arm-msm, open list

Hi Ulf,

Sorry for the late response.
See my reply below.

Thanks,
Maya

On Thu, December 6, 2012 2:18 am, Ulf Hansson wrote:
> Hi Maya,
>
> On 4 December 2012 22:17,  <merez@codeaurora.org> 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.

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.

>
>> 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 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.

>>
>> 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.
>
> 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.
>
Again, this is specific to our controller, so you can just ignore that.

>> 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
>>
>> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>>> 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.
>>>>>
>>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>>> 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.
>>>
>>> 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.
>> 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.
>
>>>
>>>>>> 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.
>>>
>>>>>> 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 <merez@codeaurora.org>
>>>>>>
>>>>>> ---
>>>>>> 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(-)
>>>>>>
>>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>>>> index 172a768..40b4ae3 100644
>>>>>> --- a/drivers/mmc/card/block.c
>>>>>> +++ b/drivers/mmc/card/block.c
>>>>>> @@ -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);
>>>>> 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
>>>>>
>>>>>> +            }
>>>>>> +    }
>>>>>>
>>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>>      if (ret) {
>>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>>>> index fadf52e..9d0c96a 100644
>>>>>> --- a/drivers/mmc/card/queue.c
>>>>>> +++ b/drivers/mmc/card/queue.c
>>>>>> @@ -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);
>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>> index 06c42cf..72ae15b 100644
>>>>>> --- a/drivers/mmc/core/core.c
>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>> @@ -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.
>>>
>>> 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.
>>>
>>>>>> + * 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);
>>>
>>> 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?
>> 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.
>>>
>>>>>>                      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);
>>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>>> index 7cc4638..dba76e3 100644
>>>>>> --- a/drivers/mmc/core/mmc.c
>>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>>> @@ -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);
>>>
>>> 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.
>>>
>>>>>> +
>>>>>> +                    /*
>>>>>> +                     * 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;
>>>>>>
>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>> index 943550d..224e2a5 100644
>>>>>> --- a/include/linux/mmc/card.h
>>>>>> +++ b/include/linux/mmc/card.h
>>>>>> @@ -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;
>>>
>>> As stated earlier, what is this timeout you are referring to? What
>>> suspend?
>> Explained above.
>>>
>>>>>> +    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;
>>>
>>> Could not find "sectors_changed" being used. Maybe you forgot to
>>> remove this from previous version of the patch.
>> Yes, this should be removed.
>>>
>>>>>> +};
>>>>>> +
>>>>>>  /*
>>>>>>   * 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;
>>>>>>  };
>>>>>>
>>>>>>  /*
>>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>>> index 9b9cdaf..665d345 100644
>>>>>> --- a/include/linux/mmc/core.h
>>>>>> +++ b/include/linux/mmc/core.h
>>>>>> @@ -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);
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> 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-kernel"
>>>> in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> 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 majordomo@vger.kernel.org
>>> 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
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>> in
>> the body of a message to majordomo@vger.kernel.org
>> 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 majordomo@vger.kernel.org
> 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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-12 12:32             ` merez
@ 2012-12-13 10:17               ` Ulf Hansson
  2012-12-21  8:35                   ` Maya Erez
  0 siblings, 1 reply; 88+ messages in thread
From: Ulf Hansson @ 2012-12-13 10:17 UTC (permalink / raw)
  To: merez; +Cc: Jaehoon Chung, linux-mmc, linux-arm-msm, open list

On 12 December 2012 13:32,  <merez@codeaurora.org> wrote:
> Hi Ulf,
>
> Sorry for the late response.
> See my reply below.
>
> Thanks,
> Maya
>
> On Thu, December 6, 2012 2:18 am, Ulf Hansson wrote:
>> Hi Maya,
>>
>> On 4 December 2012 22:17,  <merez@codeaurora.org> 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.
>
> 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 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.

> 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.

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? :-)

> 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.

> 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.
>
>>
>>> 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 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.
>
>>>
>>> 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.
>>
>> 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.
>>
> Again, this is specific to our controller, so you can just ignore that.
>
>>> 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
>>>
>>> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>>>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>>>> 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.
>>>>>>
>>>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>>>> 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.
>>>>
>>>> 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.
>>> 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.
>>
>>>>
>>>>>>> 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.
>>>>
>>>>>>> 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 <merez@codeaurora.org>
>>>>>>>
>>>>>>> ---
>>>>>>> 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(-)
>>>>>>>
>>>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>>>>> index 172a768..40b4ae3 100644
>>>>>>> --- a/drivers/mmc/card/block.c
>>>>>>> +++ b/drivers/mmc/card/block.c
>>>>>>> @@ -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);
>>>>>> 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
>>>>>>
>>>>>>> +            }
>>>>>>> +    }
>>>>>>>
>>>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>>>      if (ret) {
>>>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>>>>> index fadf52e..9d0c96a 100644
>>>>>>> --- a/drivers/mmc/card/queue.c
>>>>>>> +++ b/drivers/mmc/card/queue.c
>>>>>>> @@ -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);
>>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>>> index 06c42cf..72ae15b 100644
>>>>>>> --- a/drivers/mmc/core/core.c
>>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>>> @@ -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.
>>>>
>>>> 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.
>>>>
>>>>>>> + * 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);
>>>>
>>>> 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?
>>> 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.
>>>>
>>>>>>>                      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);
>>>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>>>> index 7cc4638..dba76e3 100644
>>>>>>> --- a/drivers/mmc/core/mmc.c
>>>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>>>> @@ -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);
>>>>
>>>> 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.
>>>>
>>>>>>> +
>>>>>>> +                    /*
>>>>>>> +                     * 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;
>>>>>>>
>>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>>> index 943550d..224e2a5 100644
>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>> @@ -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;
>>>>
>>>> As stated earlier, what is this timeout you are referring to? What
>>>> suspend?
>>> Explained above.
>>>>
>>>>>>> +    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;
>>>>
>>>> Could not find "sectors_changed" being used. Maybe you forgot to
>>>> remove this from previous version of the patch.
>>> Yes, this should be removed.
>>>>
>>>>>>> +};
>>>>>>> +
>>>>>>>  /*
>>>>>>>   * 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;
>>>>>>>  };
>>>>>>>
>>>>>>>  /*
>>>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>>>> index 9b9cdaf..665d345 100644
>>>>>>> --- a/include/linux/mmc/core.h
>>>>>>> +++ b/include/linux/mmc/core.h
>>>>>>> @@ -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);
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> 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-kernel"
>>>>> in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> 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 majordomo@vger.kernel.org
>>>> 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
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>> in
>>> the body of a message to majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> 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
>

Kind regards
Ulf Hansson

^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-10 14:23 ` [RESEND PATCH " Konstantin Dorfman
  2012-12-12  9:26   ` Seungwon Jeon
@ 2012-12-17 12:26   ` Seungwon Jeon
  2012-12-18 16:00     ` Konstantin Dorfman
  1 sibling, 1 reply; 88+ messages in thread
From: Seungwon Jeon @ 2012-12-17 12:26 UTC (permalink / raw)
  To: 'Seungwon Jeon', 'Konstantin Dorfman', cjb
  Cc: linux-mmc, per.lkml

Hi, Konstantin.

I added comments more below.

On Wednesday, December 12, 2012, Seungwon Jeon wrote:
> On Monday, December 10, 2012, Konstantin Dorfman wrote:
> > 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 <kdorfman@codeaurora.org>
> > ---
> >
> >
> > v4:
> > 	keeps new synchronization mechanism within mmc/core
> > 	layer, so mmc/core external API's are not changed.
> > 	- context_info moved into mmc_host struct
> > 	- context_info initialized in mmc_rescan_try_freq()
> >
> > 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 |   25 +++++------
> >  drivers/mmc/card/queue.c |   27 ++++++++++-
> >  drivers/mmc/card/queue.h |    2 +
> >  drivers/mmc/core/core.c  |  112 ++++++++++++++++++++++++++++++++++++++++++++-
> >  include/linux/mmc/card.h |   12 +++++
> >  include/linux/mmc/core.h |    3 +-
> >  include/linux/mmc/host.h |   16 +++++++
> >  7 files changed, 177 insertions(+), 20 deletions(-)
> >
> > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> > index 21056b9..6fe0412 100644
> > --- a/drivers/mmc/card/block.c
> > +++ b/drivers/mmc/card/block.c
> > @@ -113,17 +113,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");
> >
> > @@ -1180,6 +1169,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
> >  	memset(brq, 0, sizeof(struct mmc_blk_request));
> >  	brq->mrq.cmd = &brq->cmd;
> >  	brq->mrq.data = &brq->data;
> > +	brq->mrq.host = card->host;
> Above setting have been added at __mmc_start_data_req function in previous sending.
> Currently there is no setting for sdio. Considering sdio case, the former would be better.
> 
> >
> >  	brq->cmd.arg = blk_rq_pos(req);
> >  	if (!mmc_card_blockaddr(card))
> > @@ -1363,9 +1353,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;
> > @@ -1374,6 +1367,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 */
Is there any case to reach here?
I didn't find it.

> >  		case MMC_BLK_SUCCESS:
> >  		case MMC_BLK_PARTIAL:
> >  			/*
> > @@ -1486,6 +1481,7 @@ 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)
> > @@ -1505,9 +1501,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;
> >  }
> >
> > diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> > index fadf52e..0c37b49 100644
> > --- a/drivers/mmc/card/queue.c
> > +++ b/drivers/mmc/card/queue.c
> > @@ -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->card->host->context_info.is_waiting_last_req = true;
> > +
Unlike normal R/W request,  request for discard/flush will be finished synchronously.
That means blk_end_request is called with 1's cycle of 'issue_fn' and request will be freed in block layer.
Currently 'mqrq_prev->req' is keeping these request and is used above condition.
Maybe, same area may be reallocated for normal R/W request after discard/flush is finished.
But we still expect discard or flush is saved in 'mq->mqrq_prev->req'.
I wonder that 'mq->mqrq_prev->req' indicates the valid area in all case.

Thanks,
Seungwon Jeon

> >  		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,8 @@ static void mmc_request_fn(struct request_queue *q)
> >  {
> >  	struct mmc_queue *mq = q->queuedata;
> >  	struct request *req;
> > +	unsigned long flags;
> > +	struct mmc_context_info *cntx;
> >
> >  	if (!mq) {
> >  		while ((req = blk_fetch_request(q)) != NULL) {
> > @@ -112,7 +122,20 @@ static void mmc_request_fn(struct request_queue *q)
> >  		return;
> >  	}
> >
> > -	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
> > +	cntx = &mq->card->host->context_info;
> > +	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(&cntx->lock, flags);
> > +		if (cntx->is_waiting_last_req) {
> > +			cntx->is_new_req = true;
> > +			wake_up_interruptible(&cntx->wait);
> > +		}
> > +		spin_unlock_irqrestore(&cntx->lock, flags);
> > +	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
> >  		wake_up_process(mq->thread);
> >  }
> >
> > diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> > index d2a1eb4..970d9e7 100644
> > --- a/drivers/mmc/card/queue.h
> > +++ b/drivers/mmc/card/queue.h
> > @@ -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;
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index aaed768..344cd3a 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -319,11 +319,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->host->context_info.is_done_rcv = true;
> > +	wake_up_interruptible(&mrq->host->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);
> > @@ -337,6 +369,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 = &host->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)
> >  {
> > @@ -426,8 +512,21 @@ 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);
> > +			}
> > +			if (error)
> > +				*error = err;
> > +			/*
> > +			 * The previous request was not completed,
> > +			 * nothing to return
> > +			 */
> > +			return NULL;
> > +		}
> >  		/*
> >  		 * Check BKOPS urgency for each R1 response
> >  		 */
> > @@ -439,7 +538,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);
> > @@ -455,6 +554,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
> >
> >  	if (error)
> >  		*error = err;
> > +
> >  	return data;
> >  }
> >  EXPORT_SYMBOL(mmc_start_req);
> > @@ -2086,6 +2186,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
> >
> >  	mmc_send_if_cond(host, host->ocr_avail);
> >
> > +	spin_lock_init(&host->context_info.lock);
> > +	host->context_info.is_new_req = false;
> > +	host->context_info.is_done_rcv = false;
> > +	host->context_info.is_waiting_last_req = false;
> > +	init_waitqueue_head(&host->context_info.wait);
> > +
> This path may be retired when mmc_rescan_try_freq is failed.
> How about putting these in mmc_add_card(mmc/core/bus.c)
> <Quote>
>         ret = device_add(&card->dev);
>         if (ret)
>                 return ret;
> -> Here seems good point.
> 
> 	mmc_card_set_present(card);
> </Quote>
> Thanks,
> Seungwon Jeon
> 
> >  	/* Order's important: probe SDIO, then SD, then MMC */
> >  	if (!mmc_attach_sdio(host))
> >  		return 0;
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 5c69315..be2500a 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -187,6 +187,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.
> >   */
> > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> > index 5bf7c22..724cc95 100644
> > --- a/include/linux/mmc/core.h
> > +++ b/include/linux/mmc/core.h
> > @@ -120,6 +120,7 @@ struct mmc_data {
> >  	s32			host_cookie;	/* host private data */
> >  };
> >
> > +struct mmc_host;
> >  struct mmc_request {
> >  	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
> >  	struct mmc_command	*cmd;
> > @@ -128,9 +129,9 @@ struct mmc_request {
> >
> >  	struct completion	completion;
> >  	void			(*done)(struct mmc_request *);/* completion function */
> > +	struct mmc_host         *host;
> >  };
> >
> > -struct mmc_host;
> >  struct mmc_card;
> >  struct mmc_async_req;
> >
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index 61a10c1..c26c180 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -177,6 +177,21 @@ struct mmc_supply {
> >  	struct regulator *vqmmc;	/* Optional Vccq supply */
> >  };
> >
> > +/**
> > + * 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_host {
> >  	struct device		*parent;
> >  	struct device		class_dev;
> > @@ -331,6 +346,7 @@ struct mmc_host {
> >  	struct dentry		*debugfs_root;
> >
> >  	struct mmc_async_req	*areq;		/* active async req */
> > +	struct mmc_context_info context_info;   /* async synchronization info */
> >
> >  #ifdef CONFIG_FAIL_MMC_REQUEST
> >  	struct fault_attr	fail_mmc_request;
> > --
> > 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 majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-17 12:26   ` Seungwon Jeon
@ 2012-12-18 16:00     ` Konstantin Dorfman
  2012-12-18 16:19       ` Chris Ball
  2012-12-20  7:39       ` Seungwon Jeon
  0 siblings, 2 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-12-18 16:00 UTC (permalink / raw)
  To: Seungwon Jeon; +Cc: cjb, linux-mmc, Per Forlin

On 12/17/2012 02:26 PM, Seungwon Jeon wrote:
> Hi, Konstantin.
> 
> I added comments more below.
I've answered below.
> 
> On Wednesday, December 12, 2012, Seungwon Jeon wrote:
>> On Monday, December 10, 2012, Konstantin Dorfman wrote:
>>> 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 <kdorfman@codeaurora.org>
>>> ---
>>>
>>>
>>> v4:
>>> 	keeps new synchronization mechanism within mmc/core
>>> 	layer, so mmc/core external API's are not changed.
>>> 	- context_info moved into mmc_host struct
>>> 	- context_info initialized in mmc_rescan_try_freq()
>>>
>>> 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 |   25 +++++------
>>>   drivers/mmc/card/queue.c |   27 ++++++++++-
>>>   drivers/mmc/card/queue.h |    2 +
>>>   drivers/mmc/core/core.c  |  112 ++++++++++++++++++++++++++++++++++++++++++++-
>>>   include/linux/mmc/card.h |   12 +++++
>>>   include/linux/mmc/core.h |    3 +-
>>>   include/linux/mmc/host.h |   16 +++++++
>>>   7 files changed, 177 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>> index 21056b9..6fe0412 100644
>>> --- a/drivers/mmc/card/block.c
>>> +++ b/drivers/mmc/card/block.c
>>> @@ -113,17 +113,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");
>>>
>>> @@ -1180,6 +1169,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
>>>   	memset(brq, 0, sizeof(struct mmc_blk_request));
>>>   	brq->mrq.cmd = &brq->cmd;
>>>   	brq->mrq.data = &brq->data;
>>> +	brq->mrq.host = card->host;
>> Above setting have been added at __mmc_start_data_req function in previous sending.
>> Currently there is no setting for sdio. Considering sdio case, the former would be better.
>>
>>>
>>>   	brq->cmd.arg = blk_rq_pos(req);
>>>   	if (!mmc_card_blockaddr(card))
>>> @@ -1363,9 +1353,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;
>>> @@ -1374,6 +1367,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 */
> Is there any case to reach here?
> I didn't find it.
> 
Correct. But since there is no "default:" in this switch, compiler
demands all values of the 'status' variable present. So, we need this
BUG() here or we need to introduce "default:".

>>>   		case MMC_BLK_SUCCESS:
>>>   		case MMC_BLK_PARTIAL:
>>>   			/*
>>> @@ -1486,6 +1481,7 @@ 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)
>>> @@ -1505,9 +1501,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;
>>>   }
>>>
>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>> index fadf52e..0c37b49 100644
>>> --- a/drivers/mmc/card/queue.c
>>> +++ b/drivers/mmc/card/queue.c
>>> @@ -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->card->host->context_info.is_waiting_last_req = true;
>>> +
> Unlike normal R/W request,  request for discard/flush will be finished synchronously.
> That means blk_end_request is called with 1's cycle of 'issue_fn' and request will be freed in block layer.
> Currently 'mqrq_prev->req' is keeping these request and is used above condition.
> Maybe, same area may be reallocated for normal R/W request after discard/flush is finished.
> But we still expect discard or flush is saved in 'mq->mqrq_prev->req'.
> I wonder that 'mq->mqrq_prev->req' indicates the valid area in all case.

It seems like a bug causing unnecessary call to `issue_fn(mq, req)` even
when `req` is NULL, because 'mq->mqrq_prev->req' that holds old control
request is not NULL. It is not directly related to the patch, it could
be fixed by cleaning 'mq->mqrq_prev->req' (for control requests). I
think this should be done in separate patch.

Also this action (context_info.is_waiting_last_req = true;) could be
moved into card/block.c: mmc_blk_issue_rq() function just for the case,
when request is data request. Do you think it will be cleaner?


>>>   		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,8 @@ static void mmc_request_fn(struct request_queue *q)
>>>   {
>>>   	struct mmc_queue *mq = q->queuedata;
>>>   	struct request *req;
>>> +	unsigned long flags;
>>> +	struct mmc_context_info *cntx;
>>>
>>>   	if (!mq) {
>>>   		while ((req = blk_fetch_request(q)) != NULL) {
>>> @@ -112,7 +122,20 @@ static void mmc_request_fn(struct request_queue *q)
>>>   		return;
>>>   	}
>>>
>>> -	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
>>> +	cntx = &mq->card->host->context_info;
>>> +	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(&cntx->lock, flags);
>>> +		if (cntx->is_waiting_last_req) {
>>> +			cntx->is_new_req = true;
>>> +			wake_up_interruptible(&cntx->wait);
>>> +		}
>>> +		spin_unlock_irqrestore(&cntx->lock, flags);
>>> +	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
>>>   		wake_up_process(mq->thread);
>>>   }
>>>
>>> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
>>> index d2a1eb4..970d9e7 100644
>>> --- a/drivers/mmc/card/queue.h
>>> +++ b/drivers/mmc/card/queue.h
>>> @@ -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;
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index aaed768..344cd3a 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -319,11 +319,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->host->context_info.is_done_rcv = true;
>>> +	wake_up_interruptible(&mrq->host->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);
>>> @@ -337,6 +369,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 = &host->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)
>>>   {
>>> @@ -426,8 +512,21 @@ 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);
>>> +			}
>>> +			if (error)
>>> +				*error = err;
>>> +			/*
>>> +			 * The previous request was not completed,
>>> +			 * nothing to return
>>> +			 */
>>> +			return NULL;
>>> +		}
>>>   		/*
>>>   		 * Check BKOPS urgency for each R1 response
>>>   		 */
>>> @@ -439,7 +538,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);
>>> @@ -455,6 +554,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
>>>
>>>   	if (error)
>>>   		*error = err;
>>> +
>>>   	return data;
>>>   }
>>>   EXPORT_SYMBOL(mmc_start_req);
>>> @@ -2086,6 +2186,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
>>>
>>>   	mmc_send_if_cond(host, host->ocr_avail);
>>>
>>> +	spin_lock_init(&host->context_info.lock);
>>> +	host->context_info.is_new_req = false;
>>> +	host->context_info.is_done_rcv = false;
>>> +	host->context_info.is_waiting_last_req = false;
>>> +	init_waitqueue_head(&host->context_info.wait);
>>> +
>> This path may be retired when mmc_rescan_try_freq is failed.
>> How about putting these in mmc_add_card(mmc/core/bus.c)
>> <Quote>
>>          ret = device_add(&card->dev);
>>          if (ret)
>>                  return ret;
>> -> Here seems good point.
It is ok to put the initialization just before calling to "device_add",
because data requests starting to arrive immediately after device_add().
I've tested this already and will post soon.
>>
>> 	mmc_card_set_present(card);
>> </Quote>
>> Thanks,
>> Seungwon Jeon
>>
>>>   	/* Order's important: probe SDIO, then SD, then MMC */
>>>   	if (!mmc_attach_sdio(host))
>>>   		return 0;
>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>> index 5c69315..be2500a 100644
>>> --- a/include/linux/mmc/card.h
>>> +++ b/include/linux/mmc/card.h
>>> @@ -187,6 +187,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.
>>>    */
>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>> index 5bf7c22..724cc95 100644
>>> --- a/include/linux/mmc/core.h
>>> +++ b/include/linux/mmc/core.h
>>> @@ -120,6 +120,7 @@ struct mmc_data {
>>>   	s32			host_cookie;	/* host private data */
>>>   };
>>>
>>> +struct mmc_host;
>>>   struct mmc_request {
>>>   	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
>>>   	struct mmc_command	*cmd;
>>> @@ -128,9 +129,9 @@ struct mmc_request {
>>>
>>>   	struct completion	completion;
>>>   	void			(*done)(struct mmc_request *);/* completion function */
>>> +	struct mmc_host         *host;
>>>   };
>>>
>>> -struct mmc_host;
>>>   struct mmc_card;
>>>   struct mmc_async_req;
>>>
>>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>>> index 61a10c1..c26c180 100644
>>> --- a/include/linux/mmc/host.h
>>> +++ b/include/linux/mmc/host.h
>>> @@ -177,6 +177,21 @@ struct mmc_supply {
>>>   	struct regulator *vqmmc;	/* Optional Vccq supply */
>>>   };
>>>
>>> +/**
>>> + * 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_host {
>>>   	struct device		*parent;
>>>   	struct device		class_dev;
>>> @@ -331,6 +346,7 @@ struct mmc_host {
>>>   	struct dentry		*debugfs_root;
>>>
>>>   	struct mmc_async_req	*areq;		/* active async req */
>>> +	struct mmc_context_info context_info;   /* async synchronization info */
>>>
>>>   #ifdef CONFIG_FAIL_MMC_REQUEST
>>>   	struct fault_attr	fail_mmc_request;
>>> --
>>> 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 majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-18 16:00     ` Konstantin Dorfman
@ 2012-12-18 16:19       ` Chris Ball
  2012-12-20  7:39       ` Seungwon Jeon
  1 sibling, 0 replies; 88+ messages in thread
From: Chris Ball @ 2012-12-18 16:19 UTC (permalink / raw)
  To: Konstantin Dorfman; +Cc: Seungwon Jeon, linux-mmc, Per Forlin

Hi,

On Tue, Dec 18 2012, Konstantin Dorfman wrote:
>>>> @@ -1374,6 +1367,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 */
>> Is there any case to reach here?
>> I didn't find it.
>> 
> Correct. But since there is no "default:" in this switch, compiler
> demands all values of the 'status' variable present. So, we need this
> BUG() here or we need to introduce "default:".

Please avoid using BUG() entirely (both here and elsewhere).  It crashes
the entire MMC stack, which means it crashes the machine entirely if
you're booted from eMMC/SD.  There is almost always something better
that can be done than crashing the machine.

default: and pr_err() sound fine here.

Thanks,

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios
  2012-12-18 16:00     ` Konstantin Dorfman
  2012-12-18 16:19       ` Chris Ball
@ 2012-12-20  7:39       ` Seungwon Jeon
  1 sibling, 0 replies; 88+ messages in thread
From: Seungwon Jeon @ 2012-12-20  7:39 UTC (permalink / raw)
  To: 'Konstantin Dorfman'; +Cc: cjb, linux-mmc, 'Per Forlin'

On Wednesday, December 19, 2012, Konstantin Dorfman wrote:
> On 12/17/2012 02:26 PM, Seungwon Jeon wrote:
> > Hi, Konstantin.
> >
> > I added comments more below.
> I've answered below.
> >
> > On Wednesday, December 12, 2012, Seungwon Jeon wrote:
> >> On Monday, December 10, 2012, Konstantin Dorfman wrote:
> >>>   /*
> >>>    * 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->card->host->context_info.is_waiting_last_req = true;
> >>> +
> > Unlike normal R/W request,  request for discard/flush will be finished synchronously.
> > That means blk_end_request is called with 1's cycle of 'issue_fn' and request will be freed in block
> layer.
> > Currently 'mqrq_prev->req' is keeping these request and is used above condition.
> > Maybe, same area may be reallocated for normal R/W request after discard/flush is finished.
> > But we still expect discard or flush is saved in 'mq->mqrq_prev->req'.
> > I wonder that 'mq->mqrq_prev->req' indicates the valid area in all case.
> 
> It seems like a bug causing unnecessary call to `issue_fn(mq, req)` even
> when `req` is NULL, because 'mq->mqrq_prev->req' that holds old control
> request is not NULL. It is not directly related to the patch, it could
> be fixed by cleaning 'mq->mqrq_prev->req' (for control requests). I
> think this should be done in separate patch.
Yes, it's different issue. I've already checked it.
But I'm seeing whether cmd_flag of 'mq->mqrq_prev->req' is valid or not after blk_end_request is completed.
As it's mentioned above, special requests are completed at once.
> 
> Also this action (context_info.is_waiting_last_req = true;) could be
> moved into card/block.c: mmc_blk_issue_rq() function just for the case,
> when request is data request. Do you think it will be cleaner?
Wherever it is moved, we need other condition to decide whether last data transfer is ongoing.
'card->host->areq' could be good condition.

> >>> @@ -2086,6 +2186,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
> >>>
> >>>   	mmc_send_if_cond(host, host->ocr_avail);
> >>>
> >>> +	spin_lock_init(&host->context_info.lock);
> >>> +	host->context_info.is_new_req = false;
> >>> +	host->context_info.is_done_rcv = false;
> >>> +	host->context_info.is_waiting_last_req = false;
> >>> +	init_waitqueue_head(&host->context_info.wait);
> >>> +
> >> This path may be retired when mmc_rescan_try_freq is failed.
> >> How about putting these in mmc_add_card(mmc/core/bus.c)
> >> <Quote>
> >>          ret = device_add(&card->dev);
> >>          if (ret)
> >>                  return ret;
> >> -> Here seems good point.
> It is ok to put the initialization just before calling to "device_add",
> because data requests starting to arrive immediately after device_add().
> I've tested this already and will post soon.
Yes, it's reasonable.

Thanks,
Seungwon Jeon
> >>
> >> 	mmc_card_set_present(card);
> >> </Quote>
> >> Thanks,
> >> Seungwon Jeon


^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-13 10:17               ` Ulf Hansson
@ 2012-12-21  8:35                   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-12-21  8:35 UTC (permalink / raw)
  To: 'Ulf Hansson'
  Cc: 'Jaehoon Chung', linux-mmc, linux-arm-msm, 'open list'

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: linux-mmc-owner@vger.kernel.org
[mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Ulf Hansson
Sent: Thursday, December 13, 2012 12:18 PM
To: merez@codeaurora.org
Cc: Jaehoon Chung; linux-mmc@vger.kernel.org; linux-arm-msm@vger.kernel.org;
open list
Subject: Re: [PATCH v3] mmc: core: Add support for idle time BKOPS

On 12 December 2012 13:32,  <merez@codeaurora.org> wrote:
> Hi Ulf,
>
> Sorry for the late response.
> See my reply below.
>
> Thanks,
> Maya
>
> On Thu, December 6, 2012 2:18 am, Ulf Hansson wrote:
>> Hi Maya,
>>
>> On 4 December 2012 22:17,  <merez@codeaurora.org> 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.
>
> 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 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.

> 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.

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? :-)

> 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.

> 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.
>
>>
>>> 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 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.
>
>>>
>>> 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.
>>
>> 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.
>>
> Again, this is specific to our controller, so you can just ignore that.
>
>>> 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
>>>
>>> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>>>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>>>> 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.
>>>>>>
>>>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>>>> 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.
>>>>
>>>> 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.
>>> 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.
>>
>>>>
>>>>>>> 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.
>>>>
>>>>>>> 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 <merez@codeaurora.org>
>>>>>>>
>>>>>>> ---
>>>>>>> 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(-)
>>>>>>>
>>>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c 
>>>>>>> index 172a768..40b4ae3 100644
>>>>>>> --- a/drivers/mmc/card/block.c
>>>>>>> +++ b/drivers/mmc/card/block.c
>>>>>>> @@ -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);
>>>>>> 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
>>>>>>
>>>>>>> +            }
>>>>>>> +    }
>>>>>>>
>>>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>>>      if (ret) {
>>>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c 
>>>>>>> index fadf52e..9d0c96a 100644
>>>>>>> --- a/drivers/mmc/card/queue.c
>>>>>>> +++ b/drivers/mmc/card/queue.c
>>>>>>> @@ -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);
>>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>>> index 06c42cf..72ae15b 100644
>>>>>>> --- a/drivers/mmc/core/core.c
>>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>>> @@ -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.
>>>>
>>>> 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.
>>>>
>>>>>>> + * 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);
>>>>
>>>> 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?
>>> 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.
>>>>
>>>>>>>                      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);
>>>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>>>> index 7cc4638..dba76e3 100644
>>>>>>> --- a/drivers/mmc/core/mmc.c
>>>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>>>> @@ -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);
>>>>
>>>> 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.
>>>>
>>>>>>> +
>>>>>>> +                    /*
>>>>>>> +                     * 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;
>>>>>>>
>>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>>> index 943550d..224e2a5 100644
>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>> @@ -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;
>>>>
>>>> As stated earlier, what is this timeout you are referring to? What
>>>> suspend?
>>> Explained above.
>>>>
>>>>>>> +    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;
>>>>
>>>> Could not find "sectors_changed" being used. Maybe you forgot to
>>>> remove this from previous version of the patch.
>>> Yes, this should be removed.
>>>>
>>>>>>> +};
>>>>>>> +
>>>>>>>  /*
>>>>>>>   * 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;
>>>>>>>  };
>>>>>>>
>>>>>>>  /*
>>>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>>>> index 9b9cdaf..665d345 100644
>>>>>>> --- a/include/linux/mmc/core.h
>>>>>>> +++ b/include/linux/mmc/core.h
>>>>>>> @@ -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);
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> 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-kernel"
>>>>> in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> 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 majordomo@vger.kernel.org
>>>> 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
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>> in
>>> the body of a message to majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> 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
>

Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH v3] mmc: core: Add support for idle time BKOPS
@ 2012-12-21  8:35                   ` Maya Erez
  0 siblings, 0 replies; 88+ messages in thread
From: Maya Erez @ 2012-12-21  8:35 UTC (permalink / raw)
  To: 'Ulf Hansson'
  Cc: 'Jaehoon Chung', linux-mmc, linux-arm-msm, 'open list'

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: linux-mmc-owner@vger.kernel.org
[mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Ulf Hansson
Sent: Thursday, December 13, 2012 12:18 PM
To: merez@codeaurora.org
Cc: Jaehoon Chung; linux-mmc@vger.kernel.org; linux-arm-msm@vger.kernel.org;
open list
Subject: Re: [PATCH v3] mmc: core: Add support for idle time BKOPS

On 12 December 2012 13:32,  <merez@codeaurora.org> wrote:
> Hi Ulf,
>
> Sorry for the late response.
> See my reply below.
>
> Thanks,
> Maya
>
> On Thu, December 6, 2012 2:18 am, Ulf Hansson wrote:
>> Hi Maya,
>>
>> On 4 December 2012 22:17,  <merez@codeaurora.org> 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.
>
> 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 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.

> 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.

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? :-)

> 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.

> 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.
>
>>
>>> 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 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.
>
>>>
>>> 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.
>>
>> 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.
>>
> Again, this is specific to our controller, so you can just ignore that.
>
>>> 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
>>>
>>> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>>>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>>>> 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.
>>>>>>
>>>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>>>> 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.
>>>>
>>>> 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.
>>> 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.
>>
>>>>
>>>>>>> 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.
>>>>
>>>>>>> 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 <merez@codeaurora.org>
>>>>>>>
>>>>>>> ---
>>>>>>> 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(-)
>>>>>>>
>>>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c 
>>>>>>> index 172a768..40b4ae3 100644
>>>>>>> --- a/drivers/mmc/card/block.c
>>>>>>> +++ b/drivers/mmc/card/block.c
>>>>>>> @@ -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);
>>>>>> 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
>>>>>>
>>>>>>> +            }
>>>>>>> +    }
>>>>>>>
>>>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>>>      if (ret) {
>>>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c 
>>>>>>> index fadf52e..9d0c96a 100644
>>>>>>> --- a/drivers/mmc/card/queue.c
>>>>>>> +++ b/drivers/mmc/card/queue.c
>>>>>>> @@ -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);
>>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>>> index 06c42cf..72ae15b 100644
>>>>>>> --- a/drivers/mmc/core/core.c
>>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>>> @@ -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.
>>>>
>>>> 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.
>>>>
>>>>>>> + * 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);
>>>>
>>>> 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?
>>> 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.
>>>>
>>>>>>>                      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);
>>>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>>>> index 7cc4638..dba76e3 100644
>>>>>>> --- a/drivers/mmc/core/mmc.c
>>>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>>>> @@ -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);
>>>>
>>>> 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.
>>>>
>>>>>>> +
>>>>>>> +                    /*
>>>>>>> +                     * 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;
>>>>>>>
>>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>>> index 943550d..224e2a5 100644
>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>> @@ -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;
>>>>
>>>> As stated earlier, what is this timeout you are referring to? What
>>>> suspend?
>>> Explained above.
>>>>
>>>>>>> +    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;
>>>>
>>>> Could not find "sectors_changed" being used. Maybe you forgot to
>>>> remove this from previous version of the patch.
>>> Yes, this should be removed.
>>>>
>>>>>>> +};
>>>>>>> +
>>>>>>>  /*
>>>>>>>   * 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;
>>>>>>>  };
>>>>>>>
>>>>>>>  /*
>>>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>>>> index 9b9cdaf..665d345 100644
>>>>>>> --- a/include/linux/mmc/core.h
>>>>>>> +++ b/include/linux/mmc/core.h
>>>>>>> @@ -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);
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> 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-kernel"
>>>>> in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> 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 majordomo@vger.kernel.org
>>>> 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
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>> in
>>> the body of a message to majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> 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
>

Kind regards
Ulf Hansson
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-21  8:35                   ` Maya Erez
  (?)
@ 2012-12-21  9:56                   ` Ulf Hansson
  2012-12-21 10:24                     ` Jaehoon Chung
  -1 siblings, 1 reply; 88+ messages in thread
From: Ulf Hansson @ 2012-12-21  9:56 UTC (permalink / raw)
  To: Maya Erez; +Cc: Jaehoon Chung, linux-mmc, linux-arm-msm, open list

On 21 December 2012 09:35, Maya Erez <merez@codeaurora.org> wrote:
> 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?

Not sure what you propose here. I guess some host cap would be needed
to be able to cope with those drivers which uses runtime PM for normal
suspend.

But, the "polling method" as such shall not be done just because of
preventing those drivers entering runtime susend state.
pm_runtime_get* must be used here, I think.
Then of course you need to poll for the BKOPS status.

>
> Thanks,
> Maya
>
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org
> [mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Ulf Hansson
> Sent: Thursday, December 13, 2012 12:18 PM
> To: merez@codeaurora.org
> Cc: Jaehoon Chung; linux-mmc@vger.kernel.org; linux-arm-msm@vger.kernel.org;
> open list
> Subject: Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
>
> On 12 December 2012 13:32,  <merez@codeaurora.org> wrote:
>> Hi Ulf,
>>
>> Sorry for the late response.
>> See my reply below.
>>
>> Thanks,
>> Maya
>>
>> On Thu, December 6, 2012 2:18 am, Ulf Hansson wrote:
>>> Hi Maya,
>>>
>>> On 4 December 2012 22:17,  <merez@codeaurora.org> 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.
>>
>> 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 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.
>
>> 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.
>
> 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? :-)
>
>> 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.
>
>> 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.
>>
>>>
>>>> 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 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.
>>
>>>>
>>>> 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.
>>>
>>> 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.
>>>
>> Again, this is specific to our controller, so you can just ignore that.
>>
>>>> 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
>>>>
>>>> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>>>>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>>>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>>>>> 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.
>>>>>>>
>>>>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>>>>> 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.
>>>>>
>>>>> 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.
>>>> 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.
>>>
>>>>>
>>>>>>>> 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.
>>>>>
>>>>>>>> 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 <merez@codeaurora.org>
>>>>>>>>
>>>>>>>> ---
>>>>>>>> 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(-)
>>>>>>>>
>>>>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>>>>>> index 172a768..40b4ae3 100644
>>>>>>>> --- a/drivers/mmc/card/block.c
>>>>>>>> +++ b/drivers/mmc/card/block.c
>>>>>>>> @@ -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);
>>>>>>> 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
>>>>>>>
>>>>>>>> +            }
>>>>>>>> +    }
>>>>>>>>
>>>>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>>>>      if (ret) {
>>>>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>>>>>> index fadf52e..9d0c96a 100644
>>>>>>>> --- a/drivers/mmc/card/queue.c
>>>>>>>> +++ b/drivers/mmc/card/queue.c
>>>>>>>> @@ -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);
>>>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>>>> index 06c42cf..72ae15b 100644
>>>>>>>> --- a/drivers/mmc/core/core.c
>>>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>>>> @@ -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.
>>>>>
>>>>> 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.
>>>>>
>>>>>>>> + * 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);
>>>>>
>>>>> 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?
>>>> 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.
>>>>>
>>>>>>>>                      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);
>>>>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>>>>> index 7cc4638..dba76e3 100644
>>>>>>>> --- a/drivers/mmc/core/mmc.c
>>>>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>>>>> @@ -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);
>>>>>
>>>>> 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.
>>>>>
>>>>>>>> +
>>>>>>>> +                    /*
>>>>>>>> +                     * 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;
>>>>>>>>
>>>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>>>> index 943550d..224e2a5 100644
>>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>>> @@ -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;
>>>>>
>>>>> As stated earlier, what is this timeout you are referring to? What
>>>>> suspend?
>>>> Explained above.
>>>>>
>>>>>>>> +    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;
>>>>>
>>>>> Could not find "sectors_changed" being used. Maybe you forgot to
>>>>> remove this from previous version of the patch.
>>>> Yes, this should be removed.
>>>>>
>>>>>>>> +};
>>>>>>>> +
>>>>>>>>  /*
>>>>>>>>   * 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;
>>>>>>>>  };
>>>>>>>>
>>>>>>>>  /*
>>>>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>>>>> index 9b9cdaf..665d345 100644
>>>>>>>> --- a/include/linux/mmc/core.h
>>>>>>>> +++ b/include/linux/mmc/core.h
>>>>>>>> @@ -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);
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> 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-kernel"
>>>>>> in
>>>>>> the body of a message to majordomo@vger.kernel.org
>>>>>> 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 majordomo@vger.kernel.org
>>>>> 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
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>>> in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> 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 majordomo@vger.kernel.org
>>> 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
>>
>
> Kind regards
> Ulf Hansson
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

Kind regards
Ulf Hansson

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
  2012-12-21  9:56                   ` Ulf Hansson
@ 2012-12-21 10:24                     ` Jaehoon Chung
  0 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2012-12-21 10:24 UTC (permalink / raw)
  To: Ulf Hansson; +Cc: Maya Erez, Jaehoon Chung, linux-mmc, linux-arm-msm, open list

Hi All,

Sorry for reply..I didn't fully read about this patch history.
So i will resend the my opinion after reading the mail-history

Best Regards,
Jaehoon Chung

On 12/21/2012 06:56 PM, Ulf Hansson wrote:
> On 21 December 2012 09:35, Maya Erez <merez@codeaurora.org> wrote:
>> 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?
> 
> Not sure what you propose here. I guess some host cap would be needed
> to be able to cope with those drivers which uses runtime PM for normal
> suspend.
> 
> But, the "polling method" as such shall not be done just because of
> preventing those drivers entering runtime susend state.
> pm_runtime_get* must be used here, I think.
> Then of course you need to poll for the BKOPS status.
> 
>>
>> Thanks,
>> Maya
>>
>> -----Original Message-----
>> From: linux-mmc-owner@vger.kernel.org
>> [mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Ulf Hansson
>> Sent: Thursday, December 13, 2012 12:18 PM
>> To: merez@codeaurora.org
>> Cc: Jaehoon Chung; linux-mmc@vger.kernel.org; linux-arm-msm@vger.kernel.org;
>> open list
>> Subject: Re: [PATCH v3] mmc: core: Add support for idle time BKOPS
>>
>> On 12 December 2012 13:32,  <merez@codeaurora.org> wrote:
>>> Hi Ulf,
>>>
>>> Sorry for the late response.
>>> See my reply below.
>>>
>>> Thanks,
>>> Maya
>>>
>>> On Thu, December 6, 2012 2:18 am, Ulf Hansson wrote:
>>>> Hi Maya,
>>>>
>>>> On 4 December 2012 22:17,  <merez@codeaurora.org> 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.
>>>
>>> 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 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.
>>
>>> 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.
>>
>> 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? :-)
>>
>>> 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.
>>
>>> 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.
>>>
>>>>
>>>>> 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 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.
>>>
>>>>>
>>>>> 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.
>>>>
>>>> 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.
>>>>
>>> Again, this is specific to our controller, so you can just ignore that.
>>>
>>>>> 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
>>>>>
>>>>> On Tue, December 4, 2012 1:52 am, Ulf Hansson wrote:
>>>>>> On 3 December 2012 10:49,  <merez@codeaurora.org> 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 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
>>>>>>> On Thu, November 29, 2012 4:40 am, Jaehoon Chung wrote:
>>>>>>>> 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.
>>>>>>>>
>>>>>>>> On 11/25/2012 08:56 PM, Maya Erez wrote:
>>>>>>>>> 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.
>>>>>>
>>>>>> 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.
>>>>> 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.
>>>>
>>>>>>
>>>>>>>>> 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.
>>>>>>
>>>>>>>>> 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 <merez@codeaurora.org>
>>>>>>>>>
>>>>>>>>> ---
>>>>>>>>> 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(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>>>>>>>>> index 172a768..40b4ae3 100644
>>>>>>>>> --- a/drivers/mmc/card/block.c
>>>>>>>>> +++ b/drivers/mmc/card/block.c
>>>>>>>>> @@ -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);
>>>>>>>> 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
>>>>>>>>
>>>>>>>>> +            }
>>>>>>>>> +    }
>>>>>>>>>
>>>>>>>>>      ret = mmc_blk_part_switch(card, md);
>>>>>>>>>      if (ret) {
>>>>>>>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>>>>>>>>> index fadf52e..9d0c96a 100644
>>>>>>>>> --- a/drivers/mmc/card/queue.c
>>>>>>>>> +++ b/drivers/mmc/card/queue.c
>>>>>>>>> @@ -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);
>>>>>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>>>>>> index 06c42cf..72ae15b 100644
>>>>>>>>> --- a/drivers/mmc/core/core.c
>>>>>>>>> +++ b/drivers/mmc/core/core.c
>>>>>>>>> @@ -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.
>>>>>>
>>>>>> 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.
>>>>>>
>>>>>>>>> + * 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);
>>>>>>
>>>>>> 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?
>>>>> 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.
>>>>>>
>>>>>>>>>                      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);
>>>>>>>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>>>>>>>> index 7cc4638..dba76e3 100644
>>>>>>>>> --- a/drivers/mmc/core/mmc.c
>>>>>>>>> +++ b/drivers/mmc/core/mmc.c
>>>>>>>>> @@ -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);
>>>>>>
>>>>>> 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.
>>>>>>
>>>>>>>>> +
>>>>>>>>> +                    /*
>>>>>>>>> +                     * 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;
>>>>>>>>>
>>>>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>>>>> index 943550d..224e2a5 100644
>>>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>>>> @@ -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;
>>>>>>
>>>>>> As stated earlier, what is this timeout you are referring to? What
>>>>>> suspend?
>>>>> Explained above.
>>>>>>
>>>>>>>>> +    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;
>>>>>>
>>>>>> Could not find "sectors_changed" being used. Maybe you forgot to
>>>>>> remove this from previous version of the patch.
>>>>> Yes, this should be removed.
>>>>>>
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>>  /*
>>>>>>>>>   * 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;
>>>>>>>>>  };
>>>>>>>>>
>>>>>>>>>  /*
>>>>>>>>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>>>>>>>>> index 9b9cdaf..665d345 100644
>>>>>>>>> --- a/include/linux/mmc/core.h
>>>>>>>>> +++ b/include/linux/mmc/core.h
>>>>>>>>> @@ -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);
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> 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-kernel"
>>>>>>> in
>>>>>>> the body of a message to majordomo@vger.kernel.org
>>>>>>> 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 majordomo@vger.kernel.org
>>>>>> 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
>>>>>
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>>>> in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> 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 majordomo@vger.kernel.org
>>>> 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
>>>
>>
>> Kind regards
>> Ulf Hansson
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
> 
> Kind regards
> Ulf Hansson
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v5] mmc: fix async request mechanism for sequential read scenarios
@ 2012-12-26  9:26 ` Konstantin Dorfman
  2012-12-28 10:16   ` Seungwon Jeon
                     ` (2 more replies)
  0 siblings, 3 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2012-12-26  9:26 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, per.lkml, tgih.jun, 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 that 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 delaying the new
request execution and increase it's latency.

This change allows to wake up the MMC thread on new request arrival.
Now once the MMC thread is woken up, a new request can be fetched and
prepared in parallel to the 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 <kdorfman@codeaurora.org>
---
v5: 	- Removed BUG() and BUG_ON() with proper error handling
	- Turn on of is_waiting_last_req flag moved from queue.c into
	  block.c (mmc_blk_issue_req())
  	- mmc_init_context_info() called from mmc_add_card(). It is
	  common point for MMC, SD, SDIO.

v4:     keeps new synchronization mechanism within mmc/core
	layer, so mmc/core external API's are not changed.
	- context_info moved into mmc_host struct
	- context_info initialized in mmc_attach_mmc()

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

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 21056b9..f79b468 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -113,17 +113,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");
 
@@ -1364,8 +1353,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
 		} else
 			areq = NULL;
 		areq = mmc_start_req(card->host, areq, (int *) &status);
-		if (!areq)
+		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;
@@ -1438,6 +1430,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
 			break;
 		case MMC_BLK_NOMEDIUM:
 			goto cmd_abort;
+		default:
+			pr_err("%s: Unhandled return value (%d)",
+					req->rq_disk->disk_name, status);
+			goto cmd_abort;
 		}
 
 		if (ret) {
@@ -1472,6 +1468,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 	int ret;
 	struct mmc_blk_data *md = mq->data;
 	struct mmc_card *card = md->queue.card;
+	struct mmc_host *host = card->host;
+	unsigned long flags;
 
 	if (req && !mq->mqrq_prev->req)
 		/* claim host only for the first request */
@@ -1486,6 +1484,7 @@ 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)
@@ -1501,11 +1500,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 			mmc_blk_issue_rw_rq(mq, NULL);
 		ret = mmc_blk_issue_flush(mq, req);
 	} else {
+		if (!req && host->areq) {
+			spin_lock_irqsave(&host->context_info.lock, flags);
+			host->context_info.is_waiting_last_req = true;
+			spin_unlock_irqrestore(&host->context_info.lock, flags);
+		}
 		ret = mmc_blk_issue_rw_rq(mq, 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;
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index fadf52e..2ab21a0 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -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.
@@ -68,6 +67,10 @@ static int mmc_queue_thread(void *d)
 		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 +106,8 @@ static void mmc_request_fn(struct request_queue *q)
 {
 	struct mmc_queue *mq = q->queuedata;
 	struct request *req;
+	unsigned long flags;
+	struct mmc_context_info *cntx;
 
 	if (!mq) {
 		while ((req = blk_fetch_request(q)) != NULL) {
@@ -112,7 +117,20 @@ static void mmc_request_fn(struct request_queue *q)
 		return;
 	}
 
-	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
+	cntx = &mq->card->host->context_info;
+	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(&cntx->lock, flags);
+		if (cntx->is_waiting_last_req) {
+			cntx->is_new_req = true;
+			wake_up_interruptible(&cntx->wait);
+		}
+		spin_unlock_irqrestore(&cntx->lock, flags);
+	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
 		wake_up_process(mq->thread);
 }
 
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d2a1eb4..e20c27b 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -27,6 +27,9 @@ struct mmc_queue {
 	struct task_struct	*thread;
 	struct semaphore	thread_sem;
 	unsigned int		flags;
+#define MMC_QUEUE_SUSPENDED	(1 << 0)
+#define MMC_QUEUE_NEW_REQUEST	(1 << 1)
+
 	int			(*issue_fn)(struct mmc_queue *, struct request *);
 	void			*data;
 	struct request_queue	*queue;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 420cb67..e219c97 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -321,6 +321,7 @@ int mmc_add_card(struct mmc_card *card)
 #ifdef CONFIG_DEBUG_FS
 	mmc_add_card_debugfs(card);
 #endif
+	mmc_init_context_info(card->host);
 
 	ret = device_add(&card->dev);
 	if (ret)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index aaed768..614e4d8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -319,11 +319,44 @@ 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 a callback to host controller driver
+ */
+static void mmc_wait_data_done(struct mmc_request *mrq)
+{
+	mrq->host->context_info.is_done_rcv = true;
+	wake_up_interruptible(&mrq->host->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
+ *
+ * Sets the done callback to be called when request is completed by the 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;
+	mrq->host = host;
+	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);
@@ -337,6 +370,62 @@ 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
+ * @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 notification arrives from the 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_async_req *next_req)
+{
+	struct mmc_command *cmd;
+	struct mmc_context_info *context_info = &host->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;
+			if (!next_req) {
+				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)
 {
@@ -426,8 +515,17 @@ 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,
+					areq);
+			if (err == MMC_BLK_NEW_REQUEST) {
+				if (error)
+					*error = err;
+				/*
+				 * The previous request was not completed,
+				 * nothing to return
+				 */
+				return NULL;
+			}
 		/*
 		 * Check BKOPS urgency for each R1 response
 		 */
@@ -439,7 +537,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);
@@ -2581,6 +2679,23 @@ int mmc_pm_notify(struct notifier_block *notify_block,
 }
 #endif
 
+/**
+ * mmc_init_context_info() - init synchronization context
+ * @host: mmc host
+ *
+ * Init struct context_info needed to implement asynchronous
+ * request mechanism, used by mmc core, host driver and mmc requests
+ * supplier.
+ */
+void mmc_init_context_info(struct mmc_host *host)
+{
+	spin_lock_init(&host->context_info.lock);
+	host->context_info.is_new_req = false;
+	host->context_info.is_done_rcv = false;
+	host->context_info.is_waiting_last_req = false;
+	init_waitqueue_head(&host->context_info.wait);
+}
+
 static int __init mmc_init(void)
 {
 	int ret;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 3bdafbc..0272b32 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -76,5 +76,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host);
 void mmc_add_card_debugfs(struct mmc_card *card);
 void mmc_remove_card_debugfs(struct mmc_card *card);
 
+void mmc_init_context_info(struct mmc_host *host);
 #endif
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 5c69315..be2500a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -187,6 +187,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.
  */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 5bf7c22..495d133 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -120,6 +120,7 @@ struct mmc_data {
 	s32			host_cookie;	/* host private data */
 };
 
+struct mmc_host;
 struct mmc_request {
 	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
 	struct mmc_command	*cmd;
@@ -128,9 +129,9 @@ struct mmc_request {
 
 	struct completion	completion;
 	void			(*done)(struct mmc_request *);/* completion function */
+	struct mmc_host		*host;
 };
 
-struct mmc_host;
 struct mmc_card;
 struct mmc_async_req;
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 61a10c1..82bf1a8 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -170,6 +170,22 @@ struct mmc_slot {
 	void *handler_priv;
 };
 
+/**
+ * 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
+ * @lock		lock to protect data fields
+ */
+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 regulator;
 
 struct mmc_supply {
@@ -331,6 +347,7 @@ struct mmc_host {
 	struct dentry		*debugfs_root;
 
 	struct mmc_async_req	*areq;		/* active async req */
+	struct mmc_context_info	context_info;	/* async synchronization info */
 
 #ifdef CONFIG_FAIL_MMC_REQUEST
 	struct fault_attr	fail_mmc_request;
-- 
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

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* RE: [PATCH v5] mmc: fix async request mechanism for sequential read scenarios
  2012-12-26  9:26 ` [PATCH v5] " Konstantin Dorfman
@ 2012-12-28 10:16   ` Seungwon Jeon
  2013-01-14 19:31     ` Chris Ball
  2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
  2013-01-30  6:30   ` Seungwon Jeon
  2 siblings, 1 reply; 88+ messages in thread
From: Seungwon Jeon @ 2012-12-28 10:16 UTC (permalink / raw)
  To: 'Konstantin Dorfman', cjb; +Cc: linux-mmc, per.lkml

I checked the changes. Thanks for your work.

Reviewed-by: Seungwon Jeon <tgih.jun@samsung.com>

On Wednesday, December 26, 2012, Konstantin Dorfman wrote:
> 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 that 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 delaying the new
> request execution and increase it's latency.
> 
> This change allows to wake up the MMC thread on new request arrival.
> Now once the MMC thread is woken up, a new request can be fetched and
> prepared in parallel to the 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 <kdorfman@codeaurora.org>
> ---
> v5: 	- Removed BUG() and BUG_ON() with proper error handling
> 	- Turn on of is_waiting_last_req flag moved from queue.c into
> 	  block.c (mmc_blk_issue_req())
>   	- mmc_init_context_info() called from mmc_add_card(). It is
> 	  common point for MMC, SD, SDIO.
> 
> v4:     keeps new synchronization mechanism within mmc/core
> 	layer, so mmc/core external API's are not changed.
> 	- context_info moved into mmc_host struct
> 	- context_info initialized in mmc_attach_mmc()
> 
> 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
> 
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index 21056b9..f79b468 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -113,17 +113,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");
> 
> @@ -1364,8 +1353,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
>  		} else
>  			areq = NULL;
>  		areq = mmc_start_req(card->host, areq, (int *) &status);
> -		if (!areq)
> +		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;
> @@ -1438,6 +1430,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
>  			break;
>  		case MMC_BLK_NOMEDIUM:
>  			goto cmd_abort;
> +		default:
> +			pr_err("%s: Unhandled return value (%d)",
> +					req->rq_disk->disk_name, status);
> +			goto cmd_abort;
>  		}
> 
>  		if (ret) {
> @@ -1472,6 +1468,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
>  	int ret;
>  	struct mmc_blk_data *md = mq->data;
>  	struct mmc_card *card = md->queue.card;
> +	struct mmc_host *host = card->host;
> +	unsigned long flags;
> 
>  	if (req && !mq->mqrq_prev->req)
>  		/* claim host only for the first request */
> @@ -1486,6 +1484,7 @@ 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)
> @@ -1501,11 +1500,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
>  			mmc_blk_issue_rw_rq(mq, NULL);
>  		ret = mmc_blk_issue_flush(mq, req);
>  	} else {
> +		if (!req && host->areq) {
> +			spin_lock_irqsave(&host->context_info.lock, flags);
> +			host->context_info.is_waiting_last_req = true;
> +			spin_unlock_irqrestore(&host->context_info.lock, flags);
> +		}
>  		ret = mmc_blk_issue_rw_rq(mq, 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;
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index fadf52e..2ab21a0 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -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.
> @@ -68,6 +67,10 @@ static int mmc_queue_thread(void *d)
>  		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 +106,8 @@ static void mmc_request_fn(struct request_queue *q)
>  {
>  	struct mmc_queue *mq = q->queuedata;
>  	struct request *req;
> +	unsigned long flags;
> +	struct mmc_context_info *cntx;
> 
>  	if (!mq) {
>  		while ((req = blk_fetch_request(q)) != NULL) {
> @@ -112,7 +117,20 @@ static void mmc_request_fn(struct request_queue *q)
>  		return;
>  	}
> 
> -	if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
> +	cntx = &mq->card->host->context_info;
> +	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(&cntx->lock, flags);
> +		if (cntx->is_waiting_last_req) {
> +			cntx->is_new_req = true;
> +			wake_up_interruptible(&cntx->wait);
> +		}
> +		spin_unlock_irqrestore(&cntx->lock, flags);
> +	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
>  		wake_up_process(mq->thread);
>  }
> 
> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
> index d2a1eb4..e20c27b 100644
> --- a/drivers/mmc/card/queue.h
> +++ b/drivers/mmc/card/queue.h
> @@ -27,6 +27,9 @@ struct mmc_queue {
>  	struct task_struct	*thread;
>  	struct semaphore	thread_sem;
>  	unsigned int		flags;
> +#define MMC_QUEUE_SUSPENDED	(1 << 0)
> +#define MMC_QUEUE_NEW_REQUEST	(1 << 1)
> +
>  	int			(*issue_fn)(struct mmc_queue *, struct request *);
>  	void			*data;
>  	struct request_queue	*queue;
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index 420cb67..e219c97 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -321,6 +321,7 @@ int mmc_add_card(struct mmc_card *card)
>  #ifdef CONFIG_DEBUG_FS
>  	mmc_add_card_debugfs(card);
>  #endif
> +	mmc_init_context_info(card->host);
> 
>  	ret = device_add(&card->dev);
>  	if (ret)
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index aaed768..614e4d8 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -319,11 +319,44 @@ 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 a callback to host controller driver
> + */
> +static void mmc_wait_data_done(struct mmc_request *mrq)
> +{
> +	mrq->host->context_info.is_done_rcv = true;
> +	wake_up_interruptible(&mrq->host->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
> + *
> + * Sets the done callback to be called when request is completed by the 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;
> +	mrq->host = host;
> +	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);
> @@ -337,6 +370,62 @@ 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
> + * @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 notification arrives from the 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_async_req *next_req)
> +{
> +	struct mmc_command *cmd;
> +	struct mmc_context_info *context_info = &host->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;
> +			if (!next_req) {
> +				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)
>  {
> @@ -426,8 +515,17 @@ 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,
> +					areq);
> +			if (err == MMC_BLK_NEW_REQUEST) {
> +				if (error)
> +					*error = err;
> +				/*
> +				 * The previous request was not completed,
> +				 * nothing to return
> +				 */
> +				return NULL;
> +			}
>  		/*
>  		 * Check BKOPS urgency for each R1 response
>  		 */
> @@ -439,7 +537,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);
> @@ -2581,6 +2679,23 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>  }
>  #endif
> 
> +/**
> + * mmc_init_context_info() - init synchronization context
> + * @host: mmc host
> + *
> + * Init struct context_info needed to implement asynchronous
> + * request mechanism, used by mmc core, host driver and mmc requests
> + * supplier.
> + */
> +void mmc_init_context_info(struct mmc_host *host)
> +{
> +	spin_lock_init(&host->context_info.lock);
> +	host->context_info.is_new_req = false;
> +	host->context_info.is_done_rcv = false;
> +	host->context_info.is_waiting_last_req = false;
> +	init_waitqueue_head(&host->context_info.wait);
> +}
> +
>  static int __init mmc_init(void)
>  {
>  	int ret;
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index 3bdafbc..0272b32 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -76,5 +76,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host);
>  void mmc_add_card_debugfs(struct mmc_card *card);
>  void mmc_remove_card_debugfs(struct mmc_card *card);
> 
> +void mmc_init_context_info(struct mmc_host *host);
>  #endif
> 
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 5c69315..be2500a 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -187,6 +187,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.
>   */
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 5bf7c22..495d133 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -120,6 +120,7 @@ struct mmc_data {
>  	s32			host_cookie;	/* host private data */
>  };
> 
> +struct mmc_host;
>  struct mmc_request {
>  	struct mmc_command	*sbc;		/* SET_BLOCK_COUNT for multiblock */
>  	struct mmc_command	*cmd;
> @@ -128,9 +129,9 @@ struct mmc_request {
> 
>  	struct completion	completion;
>  	void			(*done)(struct mmc_request *);/* completion function */
> +	struct mmc_host		*host;
>  };
> 
> -struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
> 
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 61a10c1..82bf1a8 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -170,6 +170,22 @@ struct mmc_slot {
>  	void *handler_priv;
>  };
> 
> +/**
> + * 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
> + * @lock		lock to protect data fields
> + */
> +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 regulator;
> 
>  struct mmc_supply {
> @@ -331,6 +347,7 @@ struct mmc_host {
>  	struct dentry		*debugfs_root;
> 
>  	struct mmc_async_req	*areq;		/* active async req */
> +	struct mmc_context_info	context_info;	/* async synchronization info */
> 
>  #ifdef CONFIG_FAIL_MMC_REQUEST
>  	struct fault_attr	fail_mmc_request;
> --
> 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH v5 0/3] Add support for periodic BKOPS
@ 2013-01-10 20:15 ` Maya Erez
  2013-01-11 11:33   ` Jaehoon Chung
  0 siblings, 1 reply; 88+ messages in thread
From: Maya Erez @ 2013-01-10 20:15 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-arm-msm, 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 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 can be set by the host
controller. If this time is not set, a default time is used.
If the card raised an exception with need for urgent BKOPS (level 2/3)
a flag will be set to indicate MMC to start the BKOPS activity when it
becomes idle.

Since running the BKOPS too often can impact the eMMC endurance, the card
need for BKOPS is not checked every time MMC is idle (despite of cases of
exception raised). In order to estimate when is the best time to check
for BKOPS need the host will take into account the card capacity and
percentages of changed sectors in the card. A future enhancement can be to
check the card need for BKOPS only in case of random activity.

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:
- 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 v5:
    - Do not allow BKOPS operation in all levels to be blocking
    - Change the periodic check for BKOPS need to be based on percentage of changed sector
    - Add BKOPS statistics

Changes in v4:
    - Separate the polling for BKOPS completion to a different patch
    - add a flag to indicate if polling for card completion is required

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

Maya Erez (3):
  mmc: core: Add support for idle time BKOPS
  mmc: allow the host controller to poll for BKOPS completion
  mmc: core: Add MMC BKOPS statistics and debugfs ability to print them

 Documentation/mmc/mmc-dev-attrs.txt |    9 +
 drivers/mmc/card/block.c            |   96 ++++++++++++-
 drivers/mmc/card/queue.c            |    2 +
 drivers/mmc/core/bus.c              |    2 +
 drivers/mmc/core/core.c             |  286 +++++++++++++++++++++++++++++++----
 drivers/mmc/core/debugfs.c          |  114 ++++++++++++++
 drivers/mmc/core/mmc.c              |   20 +++
 include/linux/mmc/card.h            |   64 ++++++++-
 include/linux/mmc/core.h            |    5 +
 include/linux/mmc/host.h            |    2 +-
 10 files changed, 563 insertions(+), 37 deletions(-)

-- 
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

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v5 0/3] Add support for periodic BKOPS
  2013-01-10 20:15 ` [PATCH v5 0/3] Add support for periodic BKOPS Maya Erez
@ 2013-01-11 11:33   ` Jaehoon Chung
  0 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2013-01-11 11:33 UTC (permalink / raw)
  To: Maya Erez; +Cc: linux-mmc, linux-arm-msm

Hi Maya,

I didn't see about "[PATCH v5 1/3]mmc: core: Add support for idle time BKOPS" at mailing.
Could you check the patch set?

Best Regards,
Jaehoon Chung

On 01/11/2013 05:15 AM, Maya Erez wrote:
> 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 can be set by the host
> controller. If this time is not set, a default time is used.
> If the card raised an exception with need for urgent BKOPS (level 2/3)
> a flag will be set to indicate MMC to start the BKOPS activity when it
> becomes idle.
> 
> Since running the BKOPS too often can impact the eMMC endurance, the card
> need for BKOPS is not checked every time MMC is idle (despite of cases of
> exception raised). In order to estimate when is the best time to check
> for BKOPS need the host will take into account the card capacity and
> percentages of changed sectors in the card. A future enhancement can be to
> check the card need for BKOPS only in case of random activity.
> 
> 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:
> - 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 v5:
>     - Do not allow BKOPS operation in all levels to be blocking
>     - Change the periodic check for BKOPS need to be based on percentage of changed sector
>     - Add BKOPS statistics
> 
> Changes in v4:
>     - Separate the polling for BKOPS completion to a different patch
>     - add a flag to indicate if polling for card completion is required
> 
> 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
> 
> Maya Erez (3):
>   mmc: core: Add support for idle time BKOPS
>   mmc: allow the host controller to poll for BKOPS completion
>   mmc: core: Add MMC BKOPS statistics and debugfs ability to print them
> 
>  Documentation/mmc/mmc-dev-attrs.txt |    9 +
>  drivers/mmc/card/block.c            |   96 ++++++++++++-
>  drivers/mmc/card/queue.c            |    2 +
>  drivers/mmc/core/bus.c              |    2 +
>  drivers/mmc/core/core.c             |  286 +++++++++++++++++++++++++++++++----
>  drivers/mmc/core/debugfs.c          |  114 ++++++++++++++
>  drivers/mmc/core/mmc.c              |   20 +++
>  include/linux/mmc/card.h            |   64 ++++++++-
>  include/linux/mmc/core.h            |    5 +
>  include/linux/mmc/host.h            |    2 +-
>  10 files changed, 563 insertions(+), 37 deletions(-)
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v5] mmc: fix async request mechanism for sequential read scenarios
  2012-12-28 10:16   ` Seungwon Jeon
@ 2013-01-14 19:31     ` Chris Ball
  2013-01-15 12:00       ` Konstantin Dorfman
  0 siblings, 1 reply; 88+ messages in thread
From: Chris Ball @ 2013-01-14 19:31 UTC (permalink / raw)
  To: Seungwon Jeon; +Cc: 'Konstantin Dorfman', linux-mmc, per.lkml

Hi Konstantin,

On Fri, Dec 28 2012, Seungwon Jeon wrote:
> I checked the changes. Thanks for your work.
>
> Reviewed-by: Seungwon Jeon <tgih.jun@samsung.com>
>
> On Wednesday, December 26, 2012, Konstantin Dorfman wrote:
>> 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 that 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 delaying the new
>> request execution and increase it's latency.
>> 
>> This change allows to wake up the MMC thread on new request arrival.
>> Now once the MMC thread is woken up, a new request can be fetched and
>> prepared in parallel to the 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 <kdorfman@codeaurora.org>
>> ---
>> v5: 	- Removed BUG() and BUG_ON() with proper error handling
>> 	- Turn on of is_waiting_last_req flag moved from queue.c into
>> 	  block.c (mmc_blk_issue_req())
>>   	- mmc_init_context_info() called from mmc_add_card(). It is
>> 	  common point for MMC, SD, SDIO.

Thanks, pushed to mmc-next for 3.9.

Per, would you like to add an ACK here?

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH v5] mmc: fix async request mechanism for sequential read scenarios
  2013-01-14 19:31     ` Chris Ball
@ 2013-01-15 12:00       ` Konstantin Dorfman
  0 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2013-01-15 12:00 UTC (permalink / raw)
  To: Chris Ball; +Cc: Seungwon Jeon, linux-mmc, per.lkml

On 01/14/2013 09:31 PM, Chris Ball wrote:
> Hi Konstantin,
>
> On Fri, Dec 28 2012, Seungwon Jeon wrote:
>> I checked the changes. Thanks for your work.
>>
>> Reviewed-by: Seungwon Jeon <tgih.jun@samsung.com>
>>
>> On Wednesday, December 26, 2012, Konstantin Dorfman wrote:
>>> 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 that 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 delaying the new
>>> request execution and increase it's latency.
>>>
>>> This change allows to wake up the MMC thread on new request arrival.
>>> Now once the MMC thread is woken up, a new request can be fetched and
>>> prepared in parallel to the 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 <kdorfman@codeaurora.org>
>>> ---
>>> v5: 	- Removed BUG() and BUG_ON() with proper error handling
>>> 	- Turn on of is_waiting_last_req flag moved from queue.c into
>>> 	  block.c (mmc_blk_issue_req())
>>>    	- mmc_init_context_info() called from mmc_add_card(). It is
>>> 	  common point for MMC, SD, SDIO.
>
> Thanks, pushed to mmc-next for 3.9.

Great Chris,
Thanks,


-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2012-12-26  9:26 ` [PATCH v5] " Konstantin Dorfman
  2012-12-28 10:16   ` Seungwon Jeon
@ 2013-01-22 10:48   ` Seungwon Jeon
  2013-01-31 11:05     ` Jaehoon Chung
                       ` (2 more replies)
  2013-01-30  6:30   ` Seungwon Jeon
  2 siblings, 3 replies; 88+ messages in thread
From: Seungwon Jeon @ 2013-01-22 10:48 UTC (permalink / raw)
  To: linux-mmc; +Cc: 'Chris Ball', 'Konstantin Dorfman'

This patch is derived from 'mmc: fix async request mechanism ...'.
According as async transfer, a request is handled with twice mmc_start_req.
When the card is removed, the request is actually not issued in the first
mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
will come in the next mmc_start_req. But there is no event for completions.
wake_up_interruptible is needed in __mmc_start_data_req for the case of
removed card.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
---
 drivers/mmc/core/core.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 8b3a122..997b257 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
 	mrq->host = host;
 	if (mmc_card_removed(host->card)) {
 		mrq->cmd->error = -ENOMEDIUM;
+		mmc_wait_data_done(mrq);
 		return -ENOMEDIUM;
 	}
 	mmc_start_request(host, mrq);
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* RE: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2012-12-26  9:26 ` [PATCH v5] " Konstantin Dorfman
  2012-12-28 10:16   ` Seungwon Jeon
  2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
@ 2013-01-30  6:30   ` Seungwon Jeon
  2013-01-31  6:53     ` Subhash Jadavani
                       ` (2 more replies)
  2 siblings, 3 replies; 88+ messages in thread
From: Seungwon Jeon @ 2013-01-30  6:30 UTC (permalink / raw)
  To: 'Konstantin Dorfman'
  Cc: 'Chris Ball', linux-mmc, 'Seungwon Jeon'

Hi Konstantin.

Could you check this patch with [2/2]?
[PATCH 2/2] mmc: block: don't start new request when the card is removed

mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.

Thanks,
Seungwon Jeon

On Tuesday, January 22, 2013, Seungwon Jeon wrote:
> This patch is derived from 'mmc: fix async request mechanism ...'.
> According as async transfer, a request is handled with twice mmc_start_req.
> When the card is removed, the request is actually not issued in the first
> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
> will come in the next mmc_start_req. But there is no event for completions.
> wake_up_interruptible is needed in __mmc_start_data_req for the case of
> removed card.
> 
> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
> ---
>  drivers/mmc/core/core.c |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 8b3a122..997b257 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>  	mrq->host = host;
>  	if (mmc_card_removed(host->card)) {
>  		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_wait_data_done(mrq);
>  		return -ENOMEDIUM;
>  	}
>  	mmc_start_request(host, mrq);
> --
> 1.7.0.4



^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-30  6:30   ` Seungwon Jeon
@ 2013-01-31  6:53     ` Subhash Jadavani
  2013-02-04 11:43     ` Subhash Jadavani
  2013-02-04 19:27     ` Konstantin Dorfman
  2 siblings, 0 replies; 88+ messages in thread
From: Subhash Jadavani @ 2013-01-31  6:53 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: 'Konstantin Dorfman', 'Chris Ball', linux-mmc

Looks good to me.
Reviewed-by: Subhash Jadavani <subhashj@codeaurora.org>


On 1/30/2013 12:00 PM, Seungwon Jeon wrote:
> Hi Konstantin.
>
> Could you check this patch with [2/2]?
> [PATCH 2/2] mmc: block: don't start new request when the card is removed
>
> mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
> As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.
>
> Thanks,
> Seungwon Jeon
>
> On Tuesday, January 22, 2013, Seungwon Jeon wrote:
>> This patch is derived from 'mmc: fix async request mechanism ...'.
>> According as async transfer, a request is handled with twice mmc_start_req.
>> When the card is removed, the request is actually not issued in the first
>> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
>> will come in the next mmc_start_req. But there is no event for completions.
>> wake_up_interruptible is needed in __mmc_start_data_req for the case of
>> removed card.
>>
>> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
>> ---
>>  drivers/mmc/core/core.c |    1 +
>>  1 files changed, 1 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 8b3a122..997b257 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>>  	mrq->host = host;
>>  	if (mmc_card_removed(host->card)) {
>>  		mrq->cmd->error = -ENOMEDIUM;
>> +		mmc_wait_data_done(mrq);
>>  		return -ENOMEDIUM;
>>  	}
>>  	mmc_start_request(host, mrq);
>> --
>> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
@ 2013-01-31 11:05     ` Jaehoon Chung
  2013-01-31 11:25       ` Seungwon Jeon
  2013-02-08 12:07     ` Konstantin Dorfman
  2013-02-11 17:00     ` Chris Ball
  2 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2013-01-31 11:05 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: linux-mmc, 'Chris Ball', 'Konstantin Dorfman'

Great..

I also found this problem and sent the similar patch at mailing.
But i think this patch is more generic.

Acked-by: Jaehoon Chung <jh80.chung@samsung.com>

On 01/22/2013 07:48 PM, Seungwon Jeon wrote:
> This patch is derived from 'mmc: fix async request mechanism ...'.
> According as async transfer, a request is handled with twice mmc_start_req.
> When the card is removed, the request is actually not issued in the first
> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
> will come in the next mmc_start_req. But there is no event for completions.
> wake_up_interruptible is needed in __mmc_start_data_req for the case of
> removed card.
> 
> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
> ---
>  drivers/mmc/core/core.c |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 8b3a122..997b257 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>  	mrq->host = host;
>  	if (mmc_card_removed(host->card)) {
>  		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_wait_data_done(mrq);
>  		return -ENOMEDIUM;
>  	}
>  	mmc_start_request(host, mrq);
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-31 11:05     ` Jaehoon Chung
@ 2013-01-31 11:25       ` Seungwon Jeon
  0 siblings, 0 replies; 88+ messages in thread
From: Seungwon Jeon @ 2013-01-31 11:25 UTC (permalink / raw)
  To: 'Jaehoon Chung'
  Cc: linux-mmc, 'Chris Ball', 'Konstantin Dorfman',
	'Subhash Jadavani'

I hope that you find another patch.
[PATCH 2/2] mmc: block: don't start new request when the card is removed
I'd appreciate if you can review it.

Thanks,
Seungwon Jeon

On Thursday, January 31, 2013, Jaehoon Chung wrote:
> Great..
> 
> I also found this problem and sent the similar patch at mailing.
> But i think this patch is more generic.
> 
> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
> 
> On 01/22/2013 07:48 PM, Seungwon Jeon wrote:
> > This patch is derived from 'mmc: fix async request mechanism ...'.
> > According as async transfer, a request is handled with twice mmc_start_req.
> > When the card is removed, the request is actually not issued in the first
> > mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
> > will come in the next mmc_start_req. But there is no event for completions.
> > wake_up_interruptible is needed in __mmc_start_data_req for the case of
> > removed card.
> >
> > Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
> > ---
> >  drivers/mmc/core/core.c |    1 +
> >  1 files changed, 1 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 8b3a122..997b257 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
> >  	mrq->host = host;
> >  	if (mmc_card_removed(host->card)) {
> >  		mrq->cmd->error = -ENOMEDIUM;
> > +		mmc_wait_data_done(mrq);
> >  		return -ENOMEDIUM;
> >  	}
> >  	mmc_start_request(host, mrq);
> >
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-30  6:30   ` Seungwon Jeon
  2013-01-31  6:53     ` Subhash Jadavani
@ 2013-02-04 11:43     ` Subhash Jadavani
  2013-02-05  5:57       ` Seungwon Jeon
  2013-02-04 19:27     ` Konstantin Dorfman
  2 siblings, 1 reply; 88+ messages in thread
From: Subhash Jadavani @ 2013-02-04 11:43 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: 'Konstantin Dorfman', 'Chris Ball', linux-mmc

On 1/30/2013 12:00 PM, Seungwon Jeon wrote:
> Hi Konstantin.
>
> Could you check this patch with [2/2]?
> [PATCH 2/2] mmc: block: don't start new request when the card is removed
>
> mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
> As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.
>
> Thanks,
> Seungwon Jeon
>
> On Tuesday, January 22, 2013, Seungwon Jeon wrote:
>> This patch is derived from 'mmc: fix async request mechanism ...'.
>> According as async transfer, a request is handled with twice mmc_start_req.
>> When the card is removed, the request is actually not issued in the first
>> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
>> will come in the next mmc_start_req. But there is no event for completions.
>> wake_up_interruptible is needed in __mmc_start_data_req for the case of
>> removed card.

Hi Seungwon,

I looked at this again and i guess there is something wrong with
mmc_start_req() itself.
As per your commit text, first call to mmc_start_req() calls the
__mmc_start_data_req() function and __mmc_start_data_req() returns the
-ENOMEDIUM error (as card is removed) without starting the request on
host controller. so now in mmc_start_req(), "start_err" should be set.
But currently mmc_start_req() incorrectly marks the "host->areq" to
"areq" in even if the start_err is set which i guess is wrong. what do
you think about it?

So how about this fix? I guess this is better and it should fix the
deadlock issue as well. Do let me know you thoughts on this. If it looks
reasonable, i can post the formal patch.


diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 39f28af..1aa7dbe 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -546,7 +546,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host
*host,
if ((err || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL);

- if (err)
+ if (err || start_err)
host->areq = NULL;
else
host->areq = areq;

Regards,
Subhash

>>
>> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
>> ---
>>  drivers/mmc/core/core.c |    1 +
>>  1 files changed, 1 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 8b3a122..997b257 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>>  	mrq->host = host;
>>  	if (mmc_card_removed(host->card)) {
>>  		mrq->cmd->error = -ENOMEDIUM;
>> +		mmc_wait_data_done(mrq);
>>  		return -ENOMEDIUM;
>>  	}
>>  	mmc_start_request(host, mrq);
>> --
>> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-30  6:30   ` Seungwon Jeon
  2013-01-31  6:53     ` Subhash Jadavani
  2013-02-04 11:43     ` Subhash Jadavani
@ 2013-02-04 19:27     ` Konstantin Dorfman
  2 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2013-02-04 19:27 UTC (permalink / raw)
  To: Seungwon Jeon; +Cc: 'Chris Ball', linux-mmc

Hello Jeon,

I'm working on this and will post results.

Thanks,

On 01/30/2013 08:30 AM, Seungwon Jeon wrote:
> Hi Konstantin.
> 
> Could you check this patch with [2/2]?
> [PATCH 2/2] mmc: block: don't start new request when the card is removed
> 
> mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
> As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.
> 
> Thanks,
> Seungwon Jeon
> 
> On Tuesday, January 22, 2013, Seungwon Jeon wrote:
>> This patch is derived from 'mmc: fix async request mechanism ...'.
>> According as async transfer, a request is handled with twice mmc_start_req.
>> When the card is removed, the request is actually not issued in the first
>> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
>> will come in the next mmc_start_req. But there is no event for completions.
>> wake_up_interruptible is needed in __mmc_start_data_req for the case of
>> removed card.
>>
>> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
>> ---
>>   drivers/mmc/core/core.c |    1 +
>>   1 files changed, 1 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 8b3a122..997b257 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>>   	mrq->host = host;
>>   	if (mmc_card_removed(host->card)) {
>>   		mrq->cmd->error = -ENOMEDIUM;
>> +		mmc_wait_data_done(mrq);
>>   		return -ENOMEDIUM;
>>   	}
>>   	mmc_start_request(host, mrq);
>> --
>> 1.7.0.4
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-02-04 11:43     ` Subhash Jadavani
@ 2013-02-05  5:57       ` Seungwon Jeon
  2013-02-05  7:05         ` Jaehoon Chung
  0 siblings, 1 reply; 88+ messages in thread
From: Seungwon Jeon @ 2013-02-05  5:57 UTC (permalink / raw)
  To: 'Subhash Jadavani'
  Cc: 'Konstantin Dorfman', 'Chris Ball', linux-mmc

On Monday, February 04, 2013, Subhash Jadavani wrote:
> On 1/30/2013 12:00 PM, Seungwon Jeon wrote:
> > Hi Konstantin.
> >
> > Could you check this patch with [2/2]?
> > [PATCH 2/2] mmc: block: don't start new request when the card is removed
> >
> > mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
> > As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.
> >
> > Thanks,
> > Seungwon Jeon
> >
> > On Tuesday, January 22, 2013, Seungwon Jeon wrote:
> >> This patch is derived from 'mmc: fix async request mechanism ...'.
> >> According as async transfer, a request is handled with twice mmc_start_req.
> >> When the card is removed, the request is actually not issued in the first
> >> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
> >> will come in the next mmc_start_req. But there is no event for completions.
> >> wake_up_interruptible is needed in __mmc_start_data_req for the case of
> >> removed card.
> 
> Hi Seungwon,
> 
> I looked at this again and i guess there is something wrong with
> mmc_start_req() itself.
> As per your commit text, first call to mmc_start_req() calls the
> __mmc_start_data_req() function and __mmc_start_data_req() returns the
> -ENOMEDIUM error (as card is removed) without starting the request on
> host controller. so now in mmc_start_req(), "start_err" should be set.
> But currently mmc_start_req() incorrectly marks the "host->areq" to
> "areq" in even if the start_err is set which i guess is wrong. what do
> you think about it?
> 
> So how about this fix? I guess this is better and it should fix the
> deadlock issue as well. Do let me know you thoughts on this. If it looks
> reasonable, i can post the formal patch.

Hi Subhash,

I tested your fix, but there is still problem.
I didn't look into the reason.

Thanks,
Seungwon Jeon
> 
> 
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 39f28af..1aa7dbe 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -546,7 +546,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host
> *host,
> if ((err || start_err) && areq)
> mmc_post_req(host, areq->mrq, -EINVAL);
> 
> - if (err)
> + if (err || start_err)
> host->areq = NULL;
> else
> host->areq = areq;
> 
> Regards,
> Subhash
> 
> >>
> >> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
> >> ---
> >>  drivers/mmc/core/core.c |    1 +
> >>  1 files changed, 1 insertions(+), 0 deletions(-)
> >>
> >> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> >> index 8b3a122..997b257 100644
> >> --- a/drivers/mmc/core/core.c
> >> +++ b/drivers/mmc/core/core.c
> >> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
> >>  	mrq->host = host;
> >>  	if (mmc_card_removed(host->card)) {
> >>  		mrq->cmd->error = -ENOMEDIUM;
> >> +		mmc_wait_data_done(mrq);
> >>  		return -ENOMEDIUM;
> >>  	}
> >>  	mmc_start_request(host, mrq);
> >> --
> >> 1.7.0.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> > the body of a message to majordomo@vger.kernel.org
> > 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-02-05  5:57       ` Seungwon Jeon
@ 2013-02-05  7:05         ` Jaehoon Chung
  2013-02-05  7:32           ` Subhash Jadavani
  0 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2013-02-05  7:05 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: 'Subhash Jadavani', 'Konstantin Dorfman',
	'Chris Ball',
	linux-mmc

Hi Subhash,

As Mr. Seungwon mentioned, your patch didn't solve the dead-lock issue.
I'm prefered to the seungwon's patch.

Best Regards,
Jaehoon Chung

On 02/05/2013 02:57 PM, Seungwon Jeon wrote:
> On Monday, February 04, 2013, Subhash Jadavani wrote:
>> On 1/30/2013 12:00 PM, Seungwon Jeon wrote:
>>> Hi Konstantin.
>>>
>>> Could you check this patch with [2/2]?
>>> [PATCH 2/2] mmc: block: don't start new request when the card is removed
>>>
>>> mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
>>> As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.
>>>
>>> Thanks,
>>> Seungwon Jeon
>>>
>>> On Tuesday, January 22, 2013, Seungwon Jeon wrote:
>>>> This patch is derived from 'mmc: fix async request mechanism ...'.
>>>> According as async transfer, a request is handled with twice mmc_start_req.
>>>> When the card is removed, the request is actually not issued in the first
>>>> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
>>>> will come in the next mmc_start_req. But there is no event for completions.
>>>> wake_up_interruptible is needed in __mmc_start_data_req for the case of
>>>> removed card.
>>
>> Hi Seungwon,
>>
>> I looked at this again and i guess there is something wrong with
>> mmc_start_req() itself.
>> As per your commit text, first call to mmc_start_req() calls the
>> __mmc_start_data_req() function and __mmc_start_data_req() returns the
>> -ENOMEDIUM error (as card is removed) without starting the request on
>> host controller. so now in mmc_start_req(), "start_err" should be set.
>> But currently mmc_start_req() incorrectly marks the "host->areq" to
>> "areq" in even if the start_err is set which i guess is wrong. what do
>> you think about it?
>>
>> So how about this fix? I guess this is better and it should fix the
>> deadlock issue as well. Do let me know you thoughts on this. If it looks
>> reasonable, i can post the formal patch.
> 
> Hi Subhash,
> 
> I tested your fix, but there is still problem.
> I didn't look into the reason.
> 
> Thanks,
> Seungwon Jeon
>>
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 39f28af..1aa7dbe 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -546,7 +546,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host
>> *host,
>> if ((err || start_err) && areq)
>> mmc_post_req(host, areq->mrq, -EINVAL);
>>
>> - if (err)
>> + if (err || start_err)
>> host->areq = NULL;
>> else
>> host->areq = areq;
>>
>> Regards,
>> Subhash
>>
>>>>
>>>> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
>>>> ---
>>>>  drivers/mmc/core/core.c |    1 +
>>>>  1 files changed, 1 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>> index 8b3a122..997b257 100644
>>>> --- a/drivers/mmc/core/core.c
>>>> +++ b/drivers/mmc/core/core.c
>>>> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>>>>  	mrq->host = host;
>>>>  	if (mmc_card_removed(host->card)) {
>>>>  		mrq->cmd->error = -ENOMEDIUM;
>>>> +		mmc_wait_data_done(mrq);
>>>>  		return -ENOMEDIUM;
>>>>  	}
>>>>  	mmc_start_request(host, mrq);
>>>> --
>>>> 1.7.0.4
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>>> the body of a message to majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> 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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-02-05  7:05         ` Jaehoon Chung
@ 2013-02-05  7:32           ` Subhash Jadavani
  0 siblings, 0 replies; 88+ messages in thread
From: Subhash Jadavani @ 2013-02-05  7:32 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Seungwon Jeon, 'Konstantin Dorfman', 'Chris Ball',
	linux-mmc

On 2/5/2013 12:35 PM, Jaehoon Chung wrote:
> Hi Subhash,
>
> As Mr. Seungwon mentioned, your patch didn't solve the dead-lock issue.
> I'm prefered to the seungwon's patch.

Yes, i got the problem with the patch. I guess it's better to g ahead
with Seungwon's patch.
Looks good to me. Reviewed-by: Subhash Jadavani <subhashj@codeaurora.org>

>
> Best Regards,
> Jaehoon Chung
>
> On 02/05/2013 02:57 PM, Seungwon Jeon wrote:
>> On Monday, February 04, 2013, Subhash Jadavani wrote:
>>> On 1/30/2013 12:00 PM, Seungwon Jeon wrote:
>>>> Hi Konstantin.
>>>>
>>>> Could you check this patch with [2/2]?
>>>> [PATCH 2/2] mmc: block: don't start new request when the card is removed
>>>>
>>>> mmcqd is often sleeping with acquiring the claim(mmc_claim_host) when a card is removed.
>>>> As a result, mmc_rescan can be blocked for the insertion of a card newly. It's a dead lock.
>>>>
>>>> Thanks,
>>>> Seungwon Jeon
>>>>
>>>> On Tuesday, January 22, 2013, Seungwon Jeon wrote:
>>>>> This patch is derived from 'mmc: fix async request mechanism ...'.
>>>>> According as async transfer, a request is handled with twice mmc_start_req.
>>>>> When the card is removed, the request is actually not issued in the first
>>>>> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
>>>>> will come in the next mmc_start_req. But there is no event for completions.
>>>>> wake_up_interruptible is needed in __mmc_start_data_req for the case of
>>>>> removed card.
>>> Hi Seungwon,
>>>
>>> I looked at this again and i guess there is something wrong with
>>> mmc_start_req() itself.
>>> As per your commit text, first call to mmc_start_req() calls the
>>> __mmc_start_data_req() function and __mmc_start_data_req() returns the
>>> -ENOMEDIUM error (as card is removed) without starting the request on
>>> host controller. so now in mmc_start_req(), "start_err" should be set.
>>> But currently mmc_start_req() incorrectly marks the "host->areq" to
>>> "areq" in even if the start_err is set which i guess is wrong. what do
>>> you think about it?
>>>
>>> So how about this fix? I guess this is better and it should fix the
>>> deadlock issue as well. Do let me know you thoughts on this. If it looks
>>> reasonable, i can post the formal patch.
>> Hi Subhash,
>>
>> I tested your fix, but there is still problem.
>> I didn't look into the reason.
>>
>> Thanks,
>> Seungwon Jeon
>>>
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 39f28af..1aa7dbe 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -546,7 +546,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host
>>> *host,
>>> if ((err || start_err) && areq)
>>> mmc_post_req(host, areq->mrq, -EINVAL);
>>>
>>> - if (err)
>>> + if (err || start_err)
>>> host->areq = NULL;
>>> else
>>> host->areq = areq;
>>>
>>> Regards,
>>> Subhash
>>>
>>>>> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
>>>>> ---
>>>>>  drivers/mmc/core/core.c |    1 +
>>>>>  1 files changed, 1 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>>>> index 8b3a122..997b257 100644
>>>>> --- a/drivers/mmc/core/core.c
>>>>> +++ b/drivers/mmc/core/core.c
>>>>> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>>>>>  	mrq->host = host;
>>>>>  	if (mmc_card_removed(host->card)) {
>>>>>  		mrq->cmd->error = -ENOMEDIUM;
>>>>> +		mmc_wait_data_done(mrq);
>>>>>  		return -ENOMEDIUM;
>>>>>  	}
>>>>>  	mmc_start_request(host, mrq);
>>>>> --
>>>>> 1.7.0.4
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> 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 majordomo@vger.kernel.org
>>> 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 majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
  2013-01-31 11:05     ` Jaehoon Chung
@ 2013-02-08 12:07     ` Konstantin Dorfman
  2013-02-11 17:00     ` Chris Ball
  2 siblings, 0 replies; 88+ messages in thread
From: Konstantin Dorfman @ 2013-02-08 12:07 UTC (permalink / raw)
  To: Seungwon Jeon; +Cc: linux-mmc, 'Chris Ball'

Works for me.

Tested-by: Konstantin Dorfman <kdorfman@codeaurora.org>

On 01/22/2013 12:48 PM, Seungwon Jeon wrote:
> This patch is derived from 'mmc: fix async request mechanism ...'.
> According as async transfer, a request is handled with twice mmc_start_req.
> When the card is removed, the request is actually not issued in the first
> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
> will come in the next mmc_start_req. But there is no event for completions.
> wake_up_interruptible is needed in __mmc_start_data_req for the case of
> removed card.
> 
> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
> ---
>   drivers/mmc/core/core.c |    1 +
>   1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 8b3a122..997b257 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>   	mrq->host = host;
>   	if (mmc_card_removed(host->card)) {
>   		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_wait_data_done(mrq);
>   		return -ENOMEDIUM;
>   	}
>   	mmc_start_request(host, mrq);
> 


-- 
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal
  2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
  2013-01-31 11:05     ` Jaehoon Chung
  2013-02-08 12:07     ` Konstantin Dorfman
@ 2013-02-11 17:00     ` Chris Ball
  2 siblings, 0 replies; 88+ messages in thread
From: Chris Ball @ 2013-02-11 17:00 UTC (permalink / raw)
  To: Seungwon Jeon; +Cc: linux-mmc, 'Konstantin Dorfman'

Hi,

On Tue, Jan 22 2013, Seungwon Jeon wrote:
> This patch is derived from 'mmc: fix async request mechanism ...'.
> According as async transfer, a request is handled with twice mmc_start_req.
> When the card is removed, the request is actually not issued in the first
> mmc_start_req [__mmc_start_data_req]. And then mmc_wait_for_data_req_done
> will come in the next mmc_start_req. But there is no event for completions.
> wake_up_interruptible is needed in __mmc_start_data_req for the case of
> removed card.
>
> Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
> ---
>  drivers/mmc/core/core.c |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 8b3a122..997b257 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -350,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
>  	mrq->host = host;
>  	if (mmc_card_removed(host->card)) {
>  		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_wait_data_done(mrq);
>  		return -ENOMEDIUM;
>  	}
>  	mmc_start_request(host, mrq);

Thanks, pushed to mmc-next for 3.9.

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/3] mmc: Support FFU for eMMC v5.0
@ 2014-11-10 17:13 ` Avi Shchislowski
  2014-11-11  3:33   ` Jaehoon Chung
  0 siblings, 1 reply; 88+ messages in thread
From: Avi Shchislowski @ 2014-11-10 17:13 UTC (permalink / raw)
  To: ulf.hansson
  Cc: avi.shchislowski, cjb, linux-mmc, Alex.Lemberg, gwendal, Alex Lemberg

he Field Firmware Update (FFU) feature is new for eMMC 5.0 spec (Jedec:
 JESD84-B50.pdf)
   
 http://www.jedec.org/standards-documents/technology-focus-areas/flash-
 memory-ssds-ufs-emmc/e-mmc
 
 *New ioctl has been add:
 * [Alex Lemberg] "MMC_FFU_INVOKE - transfer the new Firmware data from user spac[Alex Lemberg]space
 	to the eMMC deivce and install the new image[Alex Lemberg] firmware.
* This solution allows to:
- Complete eMMC 5.0 FFU procedure as an atomic operation, without being interrupted by other IO requests
- Not limited Firmware data size. Using Multiple Write operations.
- Support of both EXT_CSD_MODE_OPERATION_CODES modes [Alex Lemberg]
* The solution is using "udev" device manager to transfer FW data from user space to eMMC driver [Alex Lemberg]
* Pre-existing functions from mmc_test were used in this solution.

Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>

Avi Shchislowski (3):
  mmc: Support FFU for eMMC v5.0
  mmc: Support FFU for eMMC v5.0
  mmc: Support FFU for eMMC v5.0

 drivers/mmc/card/Kconfig    |    8 +
 drivers/mmc/card/block.c    |    5 +
 drivers/mmc/card/mmc_test.c |   97 +--------
 drivers/mmc/core/Makefile   |    1 +
 drivers/mmc/core/core.c     |  128 ++++++++++++
 drivers/mmc/core/mmc.c      |    9 +
 drivers/mmc/core/mmc_ffu.c  |  487 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/card.h    |    2 +
 include/linux/mmc/core.h    |   30 +++
 include/linux/mmc/mmc.h     |    9 +
 10 files changed, 690 insertions(+), 86 deletions(-)
 create mode 100644 drivers/mmc/core/mmc_ffu.c

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 1/3] mmc: Support FFU for eMMC v5.0
@ 2014-11-10 17:13   ` Avi Shchislowski
  0 siblings, 0 replies; 88+ messages in thread
From: Avi Shchislowski @ 2014-11-10 17:13 UTC (permalink / raw)
  To: ulf.hansson
  Cc: avi.shchislowski, cjb, linux-mmc, Alex.Lemberg, gwendal,
	Alex Lemberg, open list


Add eMMC5.0 ffu ext_csd fields

Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>


diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 1eda8dd..3b86ed3 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -603,6 +603,15 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
 		card->ext_csd.data_sector_size = 512;
 	}
 
+	/* eMMC v5 or later */
+	if (card->ext_csd.rev >= 7) {
+		card->ext_csd.ffu_capable =
+			((ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) == 0x1) &&
+			((ext_csd[EXT_CSD_FW_CONFIG] & 0x1) == 0x0);
+		card->ext_csd.ffu_mode_op = ext_csd[EXT_CSD_FFU_FEATURES];
+	} else {
+		card->ext_csd.ffu_capable = false;
+	}
 out:
 	return err;
 }
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d424b9d..dd01efe 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -81,6 +81,8 @@ struct mmc_ext_csd {
 	bool			hpi_en;			/* HPI enablebit */
 	bool			hpi;			/* HPI support bit */
 	unsigned int		hpi_cmd;		/* cmd used as HPI */
+	bool			ffu_capable;		/* FFU support */
+	bool			ffu_mode_op;		/* FFU mode operation */
 	bool			bkops;		/* background support bit */
 	bool			bkops_en;	/* background enable bit */
 	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 64ec963..787787b 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -272,6 +272,9 @@ struct _mmc_csd {
  * EXT_CSD fields
  */
 
+#define EXT_CSD_FFU_STATUS		26	/* R */
+#define EXT_CSD_MODE_OPERATION_CODES	29	/* W */
+#define EXT_CSD_MODE_CONFIG		30	/* R/W */
 #define EXT_CSD_FLUSH_CACHE		32      /* W */
 #define EXT_CSD_CACHE_CTRL		33      /* R/W */
 #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
@@ -290,6 +293,7 @@ struct _mmc_csd {
 #define EXT_CSD_SANITIZE_START		165     /* W */
 #define EXT_CSD_WR_REL_PARAM		166	/* RO */
 #define EXT_CSD_RPMB_MULT		168	/* RO */
+#define EXT_CSD_FW_CONFIG		169	/* R/W */
 #define EXT_CSD_BOOT_WP			173	/* R/W */
 #define EXT_CSD_ERASE_GROUP_DEF		175	/* R/W */
 #define EXT_CSD_PART_CONFIG		179	/* R/W */
@@ -326,6 +330,11 @@ struct _mmc_csd {
 #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
 #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
 #define EXT_CSD_PWR_CL_DDR_200_360	253	/* RO */
+#define EXT_CSD_NUM_OF_FW_SEC_PROG	302	/* RO, 4 bytes */
+#define EXT_CSD_FFU_ARG			487	/* RO, 4 bytes */
+#define EXT_CSD_OPERATION_CODE_TIMEOUT	491	/* RO */
+#define EXT_CSD_FFU_FEATURES		492	/* RO */
+#define EXT_CSD_SUPPORTED_MODE		493	/* RO */
 #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
 #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
 #define EXT_CSD_MAX_PACKED_WRITES	500	/* RO */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 1/3] mmc: Support FFU for eMMC v5.0
@ 2014-11-10 17:13   ` Avi Shchislowski
  0 siblings, 0 replies; 88+ messages in thread
From: Avi Shchislowski @ 2014-11-10 17:13 UTC (permalink / raw)
  To: ulf.hansson
  Cc: avi.shchislowski, cjb, linux-mmc, Alex.Lemberg, gwendal,
	Alex Lemberg, open list


Add eMMC5.0 ffu ext_csd fields

Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>


diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 1eda8dd..3b86ed3 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -603,6 +603,15 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
 		card->ext_csd.data_sector_size = 512;
 	}
 
+	/* eMMC v5 or later */
+	if (card->ext_csd.rev >= 7) {
+		card->ext_csd.ffu_capable =
+			((ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) == 0x1) &&
+			((ext_csd[EXT_CSD_FW_CONFIG] & 0x1) == 0x0);
+		card->ext_csd.ffu_mode_op = ext_csd[EXT_CSD_FFU_FEATURES];
+	} else {
+		card->ext_csd.ffu_capable = false;
+	}
 out:
 	return err;
 }
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d424b9d..dd01efe 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -81,6 +81,8 @@ struct mmc_ext_csd {
 	bool			hpi_en;			/* HPI enablebit */
 	bool			hpi;			/* HPI support bit */
 	unsigned int		hpi_cmd;		/* cmd used as HPI */
+	bool			ffu_capable;		/* FFU support */
+	bool			ffu_mode_op;		/* FFU mode operation */
 	bool			bkops;		/* background support bit */
 	bool			bkops_en;	/* background enable bit */
 	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 64ec963..787787b 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -272,6 +272,9 @@ struct _mmc_csd {
  * EXT_CSD fields
  */
 
+#define EXT_CSD_FFU_STATUS		26	/* R */
+#define EXT_CSD_MODE_OPERATION_CODES	29	/* W */
+#define EXT_CSD_MODE_CONFIG		30	/* R/W */
 #define EXT_CSD_FLUSH_CACHE		32      /* W */
 #define EXT_CSD_CACHE_CTRL		33      /* R/W */
 #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
@@ -290,6 +293,7 @@ struct _mmc_csd {
 #define EXT_CSD_SANITIZE_START		165     /* W */
 #define EXT_CSD_WR_REL_PARAM		166	/* RO */
 #define EXT_CSD_RPMB_MULT		168	/* RO */
+#define EXT_CSD_FW_CONFIG		169	/* R/W */
 #define EXT_CSD_BOOT_WP			173	/* R/W */
 #define EXT_CSD_ERASE_GROUP_DEF		175	/* R/W */
 #define EXT_CSD_PART_CONFIG		179	/* R/W */
@@ -326,6 +330,11 @@ struct _mmc_csd {
 #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
 #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
 #define EXT_CSD_PWR_CL_DDR_200_360	253	/* RO */
+#define EXT_CSD_NUM_OF_FW_SEC_PROG	302	/* RO, 4 bytes */
+#define EXT_CSD_FFU_ARG			487	/* RO, 4 bytes */
+#define EXT_CSD_OPERATION_CODE_TIMEOUT	491	/* RO */
+#define EXT_CSD_FFU_FEATURES		492	/* RO */
+#define EXT_CSD_SUPPORTED_MODE		493	/* RO */
 #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
 #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
 #define EXT_CSD_MAX_PACKED_WRITES	500	/* RO */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 2/3]  mmc: Support FFU for eMMC v5.0
@ 2014-11-10 17:18   ` Avi Shchislowski
  0 siblings, 0 replies; 88+ messages in thread
From: Avi Shchislowski @ 2014-11-10 17:18 UTC (permalink / raw)
  To: ulf.hansson
  Cc: avi.shchislowski, cjb, linux-mmc, Alex.Lemberg, gwendal,
	Alex Lemberg, open list

eMMC5.0 FFU is using some of pre-existing mmc_test.c functionality.
In order to prevent code duplication, some code of mmc_test.c was moved to core.c and modified.

Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>

diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 0c0fc52..e6ee0a8 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -13,6 +13,7 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
 #include <linux/slab.h>
 
 #include <linux/scatterlist.h>
@@ -189,43 +190,8 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
 	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
 	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
 {
-	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
-
-	if (blocks > 1) {
-		mrq->cmd->opcode = write ?
-			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
-	} else {
-		mrq->cmd->opcode = write ?
-			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
-	}
-
-	mrq->cmd->arg = dev_addr;
-	if (!mmc_card_blockaddr(test->card))
-		mrq->cmd->arg <<= 9;
-
-	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
-
-	if (blocks == 1)
-		mrq->stop = NULL;
-	else {
-		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
-		mrq->stop->arg = 0;
-		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
-	}
-
-	mrq->data->blksz = blksz;
-	mrq->data->blocks = blocks;
-	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
-	mrq->data->sg = sg;
-	mrq->data->sg_len = sg_len;
-
-	mmc_set_data_timeout(mrq->data, test->card);
-}
-
-static int mmc_test_busy(struct mmc_command *cmd)
-{
-	return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
-		(R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
+	mmc_prepare_mrq(test->card, mrq, sg, sg_len,
+		dev_addr, blocks, blksz, write);
 }
 
 /*
@@ -233,30 +199,9 @@ static int mmc_test_busy(struct mmc_command *cmd)
  */
 static int mmc_test_wait_busy(struct mmc_test_card *test)
 {
-	int ret, busy;
-	struct mmc_command cmd = {0};
-
-	busy = 0;
-	do {
-		memset(&cmd, 0, sizeof(struct mmc_command));
-
-		cmd.opcode = MMC_SEND_STATUS;
-		cmd.arg = test->card->rca << 16;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
-
-		ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
-		if (ret)
-			break;
-
-		if (!busy && mmc_test_busy(&cmd)) {
-			busy = 1;
-			if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
-				pr_info("%s: Warning: Host did not "
-					"wait for busy state to end.\n",
-					mmc_hostname(test->card->host));
-		}
-	} while (mmc_test_busy(&cmd));
+	int ret;
 
+	ret = mmc_wait_busy(test->card);
 	return ret;
 }
 
@@ -693,19 +638,9 @@ static int mmc_test_check_result(struct mmc_test_card *test,
 {
 	int ret;
 
-	BUG_ON(!mrq || !mrq->cmd || !mrq->data);
-
 	ret = 0;
 
-	if (!ret && mrq->cmd->error)
-		ret = mrq->cmd->error;
-	if (!ret && mrq->data->error)
-		ret = mrq->data->error;
-	if (!ret && mrq->stop && mrq->stop->error)
-		ret = mrq->stop->error;
-	if (!ret && mrq->data->bytes_xfered !=
-		mrq->data->blocks * mrq->data->blksz)
-		ret = RESULT_FAIL;
+	ret = mmc_check_result(mrq);
 
 	if (ret == -EINVAL)
 		ret = RESULT_UNSUP_HOST;
@@ -844,23 +779,13 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
 	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
 	unsigned blocks, unsigned blksz, int write)
 {
-	struct mmc_request mrq = {0};
-	struct mmc_command cmd = {0};
-	struct mmc_command stop = {0};
-	struct mmc_data data = {0};
-
-	mrq.cmd = &cmd;
-	mrq.data = &data;
-	mrq.stop = &stop;
+	int ret;
 
-	mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
+	ret = mmc_simple_transfer(test->card, sg, sg_len, dev_addr,
 		blocks, blksz, write);
-
-	mmc_wait_for_req(test->card->host, &mrq);
-
-	mmc_test_wait_busy(test);
-
-	return mmc_test_check_result(test, &mrq);
+	if (ret == -EINVAL)
+		ret = RESULT_UNSUP_HOST;
+	return ret;
 }
 
 /*
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d03a080..132da0c 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2658,6 +2658,134 @@ int mmc_pm_notify(struct notifier_block *notify_block,
 	return 0;
 }
 #endif
+/*
+ * Fill in the mmc_request structure given a set of transfer parameters.
+ */
+void mmc_prepare_mrq(struct mmc_card *card,
+	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
+	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
+{
+	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
+
+	if (blocks > 1) {
+		mrq->cmd->opcode = write ?
+			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
+	} else {
+		mrq->cmd->opcode = write ?
+			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
+	}
+
+	mrq->cmd->arg = dev_addr;
+	if (!mmc_card_blockaddr(card))
+		mrq->cmd->arg <<= 9;
+
+	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	if (blocks == 1)
+		mrq->stop = NULL;
+	else {
+		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
+		mrq->stop->arg = 0;
+		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+	}
+
+	mrq->data->blksz = blksz;
+	mrq->data->blocks = blocks;
+	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+	mrq->data->sg = sg;
+	mrq->data->sg_len = sg_len;
+
+	mmc_set_data_timeout(mrq->data, card);
+}
+EXPORT_SYMBOL(mmc_prepare_mrq);
+
+static int mmc_busy(struct mmc_command *cmd)
+{
+	return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
+		(R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
+}
+
+/*
+ * Wait for the card to finish the busy state
+ */
+int mmc_wait_busy(struct mmc_card *card)
+{
+	int ret, busy = 0;
+	struct mmc_command cmd = {0};
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	cmd.opcode = MMC_SEND_STATUS;
+	cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+	do {
+		ret = mmc_wait_for_cmd(card->host, &cmd, 0);
+		if (ret)
+			break;
+
+		if (!busy && mmc_busy(&cmd)) {
+			busy = 1;
+			if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) {
+				pr_warn("%s: Warning: Host did not "
+					"wait for busy state to end.\n",
+					mmc_hostname(card->host));
+			}
+		}
+
+	} while (mmc_busy(&cmd));
+
+	return ret;
+}
+EXPORT_SYMBOL(mmc_wait_busy);
+
+int mmc_check_result(struct mmc_request *mrq)
+{
+	int ret;
+
+	BUG_ON(!mrq || !mrq->cmd || !mrq->data);
+
+	ret = 0;
+
+	if (!ret && mrq->cmd->error)
+		ret = mrq->cmd->error;
+	if (!ret && mrq->data->error)
+		ret = mrq->data->error;
+	if (!ret && mrq->stop && mrq->stop->error)
+		ret = mrq->stop->error;
+	if (!ret && mrq->data->bytes_xfered !=
+		mrq->data->blocks * mrq->data->blksz)
+		ret = -EPERM;
+
+	return ret;
+}
+EXPORT_SYMBOL(mmc_check_result);
+
+/*
++ * transfer with certain parameters
+ */
+int mmc_simple_transfer(struct mmc_card *card,
+	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
+	unsigned blocks, unsigned blksz, int write)
+{
+	struct mmc_request mrq = {0};
+	struct mmc_command cmd = {0};
+	struct mmc_command stop = {0};
+	struct mmc_data data = {0};
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+	mrq.stop = &stop;
+
+	mmc_prepare_mrq(card, &mrq, sg, sg_len, dev_addr,
+		blocks, blksz, write);
+
+	mmc_wait_for_req(card->host, &mrq);
+
+	mmc_wait_busy(card);
+
+	return mmc_check_result(&mrq);
+}
+EXPORT_SYMBOL(mmc_simple_transfer);
 
 /**
  * mmc_init_context_info() - init synchronization context
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index f206e29..92540d0 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -196,6 +196,14 @@ extern void mmc_put_card(struct mmc_card *card);
 extern int mmc_flush_cache(struct mmc_card *);
 
 extern int mmc_detect_card_removed(struct mmc_host *host);
+extern void mmc_prepare_mrq(struct mmc_card *card,
+	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
+	unsigned dev_addr, unsigned blocks, unsigned blksz, int write);
+extern int mmc_wait_busy(struct mmc_card *card);
+extern int mmc_check_result(struct mmc_request *mrq);
+extern int mmc_simple_transfer(struct mmc_card *card,
+	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
+	unsigned blocks, unsigned blksz, int write);
 
 /**
  *	mmc_claim_host - exclusively claim a host
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 2/3]  mmc: Support FFU for eMMC v5.0
@ 2014-11-10 17:18   ` Avi Shchislowski
  0 siblings, 0 replies; 88+ messages in thread
From: Avi Shchislowski @ 2014-11-10 17:18 UTC (permalink / raw)
  To: ulf.hansson
  Cc: avi.shchislowski, cjb, linux-mmc, Alex.Lemberg, gwendal,
	Alex Lemberg, open list

eMMC5.0 FFU is using some of pre-existing mmc_test.c functionality.
In order to prevent code duplication, some code of mmc_test.c was moved to core.c and modified.

Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>

diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 0c0fc52..e6ee0a8 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -13,6 +13,7 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
 #include <linux/slab.h>
 
 #include <linux/scatterlist.h>
@@ -189,43 +190,8 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
 	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
 	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
 {
-	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
-
-	if (blocks > 1) {
-		mrq->cmd->opcode = write ?
-			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
-	} else {
-		mrq->cmd->opcode = write ?
-			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
-	}
-
-	mrq->cmd->arg = dev_addr;
-	if (!mmc_card_blockaddr(test->card))
-		mrq->cmd->arg <<= 9;
-
-	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
-
-	if (blocks == 1)
-		mrq->stop = NULL;
-	else {
-		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
-		mrq->stop->arg = 0;
-		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
-	}
-
-	mrq->data->blksz = blksz;
-	mrq->data->blocks = blocks;
-	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
-	mrq->data->sg = sg;
-	mrq->data->sg_len = sg_len;
-
-	mmc_set_data_timeout(mrq->data, test->card);
-}
-
-static int mmc_test_busy(struct mmc_command *cmd)
-{
-	return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
-		(R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
+	mmc_prepare_mrq(test->card, mrq, sg, sg_len,
+		dev_addr, blocks, blksz, write);
 }
 
 /*
@@ -233,30 +199,9 @@ static int mmc_test_busy(struct mmc_command *cmd)
  */
 static int mmc_test_wait_busy(struct mmc_test_card *test)
 {
-	int ret, busy;
-	struct mmc_command cmd = {0};
-
-	busy = 0;
-	do {
-		memset(&cmd, 0, sizeof(struct mmc_command));
-
-		cmd.opcode = MMC_SEND_STATUS;
-		cmd.arg = test->card->rca << 16;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
-
-		ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
-		if (ret)
-			break;
-
-		if (!busy && mmc_test_busy(&cmd)) {
-			busy = 1;
-			if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
-				pr_info("%s: Warning: Host did not "
-					"wait for busy state to end.\n",
-					mmc_hostname(test->card->host));
-		}
-	} while (mmc_test_busy(&cmd));
+	int ret;
 
+	ret = mmc_wait_busy(test->card);
 	return ret;
 }
 
@@ -693,19 +638,9 @@ static int mmc_test_check_result(struct mmc_test_card *test,
 {
 	int ret;
 
-	BUG_ON(!mrq || !mrq->cmd || !mrq->data);
-
 	ret = 0;
 
-	if (!ret && mrq->cmd->error)
-		ret = mrq->cmd->error;
-	if (!ret && mrq->data->error)
-		ret = mrq->data->error;
-	if (!ret && mrq->stop && mrq->stop->error)
-		ret = mrq->stop->error;
-	if (!ret && mrq->data->bytes_xfered !=
-		mrq->data->blocks * mrq->data->blksz)
-		ret = RESULT_FAIL;
+	ret = mmc_check_result(mrq);
 
 	if (ret == -EINVAL)
 		ret = RESULT_UNSUP_HOST;
@@ -844,23 +779,13 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
 	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
 	unsigned blocks, unsigned blksz, int write)
 {
-	struct mmc_request mrq = {0};
-	struct mmc_command cmd = {0};
-	struct mmc_command stop = {0};
-	struct mmc_data data = {0};
-
-	mrq.cmd = &cmd;
-	mrq.data = &data;
-	mrq.stop = &stop;
+	int ret;
 
-	mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
+	ret = mmc_simple_transfer(test->card, sg, sg_len, dev_addr,
 		blocks, blksz, write);
-
-	mmc_wait_for_req(test->card->host, &mrq);
-
-	mmc_test_wait_busy(test);
-
-	return mmc_test_check_result(test, &mrq);
+	if (ret == -EINVAL)
+		ret = RESULT_UNSUP_HOST;
+	return ret;
 }
 
 /*
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d03a080..132da0c 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2658,6 +2658,134 @@ int mmc_pm_notify(struct notifier_block *notify_block,
 	return 0;
 }
 #endif
+/*
+ * Fill in the mmc_request structure given a set of transfer parameters.
+ */
+void mmc_prepare_mrq(struct mmc_card *card,
+	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
+	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
+{
+	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
+
+	if (blocks > 1) {
+		mrq->cmd->opcode = write ?
+			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
+	} else {
+		mrq->cmd->opcode = write ?
+			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
+	}
+
+	mrq->cmd->arg = dev_addr;
+	if (!mmc_card_blockaddr(card))
+		mrq->cmd->arg <<= 9;
+
+	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	if (blocks == 1)
+		mrq->stop = NULL;
+	else {
+		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
+		mrq->stop->arg = 0;
+		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+	}
+
+	mrq->data->blksz = blksz;
+	mrq->data->blocks = blocks;
+	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+	mrq->data->sg = sg;
+	mrq->data->sg_len = sg_len;
+
+	mmc_set_data_timeout(mrq->data, card);
+}
+EXPORT_SYMBOL(mmc_prepare_mrq);
+
+static int mmc_busy(struct mmc_command *cmd)
+{
+	return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
+		(R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
+}
+
+/*
+ * Wait for the card to finish the busy state
+ */
+int mmc_wait_busy(struct mmc_card *card)
+{
+	int ret, busy = 0;
+	struct mmc_command cmd = {0};
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	cmd.opcode = MMC_SEND_STATUS;
+	cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+	do {
+		ret = mmc_wait_for_cmd(card->host, &cmd, 0);
+		if (ret)
+			break;
+
+		if (!busy && mmc_busy(&cmd)) {
+			busy = 1;
+			if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) {
+				pr_warn("%s: Warning: Host did not "
+					"wait for busy state to end.\n",
+					mmc_hostname(card->host));
+			}
+		}
+
+	} while (mmc_busy(&cmd));
+
+	return ret;
+}
+EXPORT_SYMBOL(mmc_wait_busy);
+
+int mmc_check_result(struct mmc_request *mrq)
+{
+	int ret;
+
+	BUG_ON(!mrq || !mrq->cmd || !mrq->data);
+
+	ret = 0;
+
+	if (!ret && mrq->cmd->error)
+		ret = mrq->cmd->error;
+	if (!ret && mrq->data->error)
+		ret = mrq->data->error;
+	if (!ret && mrq->stop && mrq->stop->error)
+		ret = mrq->stop->error;
+	if (!ret && mrq->data->bytes_xfered !=
+		mrq->data->blocks * mrq->data->blksz)
+		ret = -EPERM;
+
+	return ret;
+}
+EXPORT_SYMBOL(mmc_check_result);
+
+/*
++ * transfer with certain parameters
+ */
+int mmc_simple_transfer(struct mmc_card *card,
+	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
+	unsigned blocks, unsigned blksz, int write)
+{
+	struct mmc_request mrq = {0};
+	struct mmc_command cmd = {0};
+	struct mmc_command stop = {0};
+	struct mmc_data data = {0};
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+	mrq.stop = &stop;
+
+	mmc_prepare_mrq(card, &mrq, sg, sg_len, dev_addr,
+		blocks, blksz, write);
+
+	mmc_wait_for_req(card->host, &mrq);
+
+	mmc_wait_busy(card);
+
+	return mmc_check_result(&mrq);
+}
+EXPORT_SYMBOL(mmc_simple_transfer);
 
 /**
  * mmc_init_context_info() - init synchronization context
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index f206e29..92540d0 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -196,6 +196,14 @@ extern void mmc_put_card(struct mmc_card *card);
 extern int mmc_flush_cache(struct mmc_card *);
 
 extern int mmc_detect_card_removed(struct mmc_host *host);
+extern void mmc_prepare_mrq(struct mmc_card *card,
+	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
+	unsigned dev_addr, unsigned blocks, unsigned blksz, int write);
+extern int mmc_wait_busy(struct mmc_card *card);
+extern int mmc_check_result(struct mmc_request *mrq);
+extern int mmc_simple_transfer(struct mmc_card *card,
+	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
+	unsigned blocks, unsigned blksz, int write);
 
 /**
  *	mmc_claim_host - exclusively claim a host
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/3] mmc: Support FFU for eMMC v5.0
  2014-11-10 17:13   ` Avi Shchislowski
  (?)
@ 2014-11-11  3:07   ` Jaehoon Chung
  2014-11-11  8:44     ` Avi Shchislowski
  -1 siblings, 1 reply; 88+ messages in thread
From: Jaehoon Chung @ 2014-11-11  3:07 UTC (permalink / raw)
  To: Avi Shchislowski, ulf.hansson
  Cc: 'Chris Ball', linux-mmc, Alex.Lemberg, gwendal, open list, CPGS

Hi,

how did you make the patch?
This patch didn't apply at Ulf's repository.


On 11/11/2014 02:13 AM, Avi Shchislowski wrote:
> 
> Add eMMC5.0 ffu ext_csd fields
> 
> Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
> Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>
> 
> 
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 1eda8dd..3b86ed3 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -603,6 +603,15 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
>  		card->ext_csd.data_sector_size = 512;
>  	}
>  
> +	/* eMMC v5 or later */
> +	if (card->ext_csd.rev >= 7) {
> +		card->ext_csd.ffu_capable =
> +			((ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) == 0x1) &&
> +			((ext_csd[EXT_CSD_FW_CONFIG] & 0x1) == 0x0);

if (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) &&  !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1))
		card->ext_csd.ffu_capable = true;

> +		card->ext_csd.ffu_mode_op = ext_csd[EXT_CSD_FFU_FEATURES];
> +	} else {
> +		card->ext_csd.ffu_capable = false;

Can be set to false by default before checking whether card is eMMC v5.0 or not.

card->ext_csd.ffu_capable = false;

if (card->ext_csd.rev >= 7) {
	...
}

Best Regards,
Jaehoon Chung

> +	}
>  out:
>  	return err;
>  }
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index d424b9d..dd01efe 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -81,6 +81,8 @@ struct mmc_ext_csd {
>  	bool			hpi_en;			/* HPI enablebit */
>  	bool			hpi;			/* HPI support bit */
>  	unsigned int		hpi_cmd;		/* cmd used as HPI */
> +	bool			ffu_capable;		/* FFU support */
> +	bool			ffu_mode_op;		/* FFU mode operation */
>  	bool			bkops;		/* background support bit */
>  	bool			bkops_en;	/* background enable bit */
>  	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 64ec963..787787b 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -272,6 +272,9 @@ struct _mmc_csd {
>   * EXT_CSD fields
>   */
>  
> +#define EXT_CSD_FFU_STATUS		26	/* R */
> +#define EXT_CSD_MODE_OPERATION_CODES	29	/* W */
> +#define EXT_CSD_MODE_CONFIG		30	/* R/W */
>  #define EXT_CSD_FLUSH_CACHE		32      /* W */
>  #define EXT_CSD_CACHE_CTRL		33      /* R/W */
>  #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
> @@ -290,6 +293,7 @@ struct _mmc_csd {
>  #define EXT_CSD_SANITIZE_START		165     /* W */
>  #define EXT_CSD_WR_REL_PARAM		166	/* RO */
>  #define EXT_CSD_RPMB_MULT		168	/* RO */
> +#define EXT_CSD_FW_CONFIG		169	/* R/W */
>  #define EXT_CSD_BOOT_WP			173	/* R/W */
>  #define EXT_CSD_ERASE_GROUP_DEF		175	/* R/W */
>  #define EXT_CSD_PART_CONFIG		179	/* R/W */
> @@ -326,6 +330,11 @@ struct _mmc_csd {
>  #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
>  #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
>  #define EXT_CSD_PWR_CL_DDR_200_360	253	/* RO */
> +#define EXT_CSD_NUM_OF_FW_SEC_PROG	302	/* RO, 4 bytes */
> +#define EXT_CSD_FFU_ARG			487	/* RO, 4 bytes */
> +#define EXT_CSD_OPERATION_CODE_TIMEOUT	491	/* RO */
> +#define EXT_CSD_FFU_FEATURES		492	/* RO */
> +#define EXT_CSD_SUPPORTED_MODE		493	/* RO */
>  #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
>  #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
>  #define EXT_CSD_MAX_PACKED_WRITES	500	/* RO */
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 2/3]  mmc: Support FFU for eMMC v5.0
  2014-11-10 17:18   ` Avi Shchislowski
  (?)
@ 2014-11-11  3:30   ` Jaehoon Chung
  -1 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2014-11-11  3:30 UTC (permalink / raw)
  To: Avi Shchislowski, ulf.hansson
  Cc: 'Chris Ball', linux-mmc, Alex.Lemberg, gwendal, open list, CPGS

Hi,

On 11/11/2014 02:18 AM, Avi Shchislowski wrote:
> eMMC5.0 FFU is using some of pre-existing mmc_test.c functionality.
> In order to prevent code duplication, some code of mmc_test.c was moved to core.c and modified.
> 
> Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
> Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>
> 
> diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
> index 0c0fc52..e6ee0a8 100644
> --- a/drivers/mmc/card/mmc_test.c
> +++ b/drivers/mmc/card/mmc_test.c
> @@ -13,6 +13,7 @@
>  #include <linux/mmc/card.h>
>  #include <linux/mmc/host.h>
>  #include <linux/mmc/mmc.h>
> +#include <linux/mmc/core.h>
>  #include <linux/slab.h>
>  
>  #include <linux/scatterlist.h>
> @@ -189,43 +190,8 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
>  	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
>  	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
>  {
> -	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
> -
> -	if (blocks > 1) {
> -		mrq->cmd->opcode = write ?
> -			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
> -	} else {
> -		mrq->cmd->opcode = write ?
> -			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
> -	}
> -
> -	mrq->cmd->arg = dev_addr;
> -	if (!mmc_card_blockaddr(test->card))
> -		mrq->cmd->arg <<= 9;
> -
> -	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> -
> -	if (blocks == 1)
> -		mrq->stop = NULL;
> -	else {
> -		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
> -		mrq->stop->arg = 0;
> -		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
> -	}
> -
> -	mrq->data->blksz = blksz;
> -	mrq->data->blocks = blocks;
> -	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
> -	mrq->data->sg = sg;
> -	mrq->data->sg_len = sg_len;
> -
> -	mmc_set_data_timeout(mrq->data, test->card);
> -}
> -
> -static int mmc_test_busy(struct mmc_command *cmd)
> -{
> -	return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
> -		(R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
> +	mmc_prepare_mrq(test->card, mrq, sg, sg_len,
> +		dev_addr, blocks, blksz, write);
>  }
>  
>  /*
> @@ -233,30 +199,9 @@ static int mmc_test_busy(struct mmc_command *cmd)
>   */
>  static int mmc_test_wait_busy(struct mmc_test_card *test)
>  {
> -	int ret, busy;
> -	struct mmc_command cmd = {0};
> -
> -	busy = 0;
> -	do {
> -		memset(&cmd, 0, sizeof(struct mmc_command));
> -
> -		cmd.opcode = MMC_SEND_STATUS;
> -		cmd.arg = test->card->rca << 16;
> -		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> -
> -		ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
> -		if (ret)
> -			break;
> -
> -		if (!busy && mmc_test_busy(&cmd)) {
> -			busy = 1;
> -			if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
> -				pr_info("%s: Warning: Host did not "
> -					"wait for busy state to end.\n",
> -					mmc_hostname(test->card->host));
> -		}
> -	} while (mmc_test_busy(&cmd));
> +	int ret;
>  
> +	ret = mmc_wait_busy(test->card);

return mmc_wait_busy(test->card);

And mmc_test_wait_busy is wrapper function?
Why do you use the wrapper function?

>  	return ret;
>  }
>  
> @@ -693,19 +638,9 @@ static int mmc_test_check_result(struct mmc_test_card *test,
>  {
>  	int ret;
>  
> -	BUG_ON(!mrq || !mrq->cmd || !mrq->data);
> -
>  	ret = 0;
>  
> -	if (!ret && mrq->cmd->error)
> -		ret = mrq->cmd->error;
> -	if (!ret && mrq->data->error)
> -		ret = mrq->data->error;
> -	if (!ret && mrq->stop && mrq->stop->error)
> -		ret = mrq->stop->error;
> -	if (!ret && mrq->data->bytes_xfered !=
> -		mrq->data->blocks * mrq->data->blksz)
> -		ret = RESULT_FAIL;
> +	ret = mmc_check_result(mrq);
>  
>  	if (ret == -EINVAL)
>  		ret = RESULT_UNSUP_HOST;
> @@ -844,23 +779,13 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
>  	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
>  	unsigned blocks, unsigned blksz, int write)
>  {
> -	struct mmc_request mrq = {0};
> -	struct mmc_command cmd = {0};
> -	struct mmc_command stop = {0};
> -	struct mmc_data data = {0};
> -
> -	mrq.cmd = &cmd;
> -	mrq.data = &data;
> -	mrq.stop = &stop;
> +	int ret;
>  
> -	mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
> +	ret = mmc_simple_transfer(test->card, sg, sg_len, dev_addr,
>  		blocks, blksz, write);
> -
> -	mmc_wait_for_req(test->card->host, &mrq);
> -
> -	mmc_test_wait_busy(test);
> -
> -	return mmc_test_check_result(test, &mrq);
> +	if (ret == -EINVAL)
> +		ret = RESULT_UNSUP_HOST;
> +	return ret;
>  }
>  
>  /*
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index d03a080..132da0c 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -2658,6 +2658,134 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>  	return 0;
>  }
>  #endif
> +/*
> + * Fill in the mmc_request structure given a set of transfer parameters.
> + */
> +void mmc_prepare_mrq(struct mmc_card *card,
> +	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
> +	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
> +{
> +	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
> +
> +	if (blocks > 1) {
> +		mrq->cmd->opcode = write ?
> +			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
> +	} else {
> +		mrq->cmd->opcode = write ?
> +			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
> +	}
> +
> +	mrq->cmd->arg = dev_addr;
> +	if (!mmc_card_blockaddr(card))
> +		mrq->cmd->arg <<= 9;
> +
> +	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> +	if (blocks == 1)
> +		mrq->stop = NULL;
> +	else {
> +		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
> +		mrq->stop->arg = 0;
> +		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
> +	}
> +
> +	mrq->data->blksz = blksz;
> +	mrq->data->blocks = blocks;
> +	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
> +	mrq->data->sg = sg;
> +	mrq->data->sg_len = sg_len;
> +
> +	mmc_set_data_timeout(mrq->data, card);
> +}
> +EXPORT_SYMBOL(mmc_prepare_mrq);
> +
> +static int mmc_busy(struct mmc_command *cmd)
> +{
> +	return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
> +		(R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
> +}

What's function?

> +
> +/*
> + * Wait for the card to finish the busy state
> + */
> +int mmc_wait_busy(struct mmc_card *card)
> +{
> +	int ret, busy = 0;
> +	struct mmc_command cmd = {0};
> +
> +	memset(&cmd, 0, sizeof(struct mmc_command));
> +	cmd.opcode = MMC_SEND_STATUS;
> +	cmd.arg = card->rca << 16;
> +	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
> +
> +	do {
> +		ret = mmc_wait_for_cmd(card->host, &cmd, 0);
> +		if (ret)
> +			break;
> +
> +		if (!busy && mmc_busy(&cmd)) {
> +			busy = 1;
> +			if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) {
> +				pr_warn("%s: Warning: Host did not "
> +					"wait for busy state to end.\n",
> +					mmc_hostname(card->host));
> +			}
> +		}
> +
> +	} while (mmc_busy(&cmd));
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mmc_wait_busy);
> +
> +int mmc_check_result(struct mmc_request *mrq)
> +{
> +	int ret;
> +
> +	BUG_ON(!mrq || !mrq->cmd || !mrq->data);
> +
> +	ret = 0;

Why do you initialized at here? 
if ret is 0, why do you check "if (!ret && xxxx)" at the below condition?

> +
> +	if (!ret && mrq->cmd->error)
> +		ret = mrq->cmd->error;
> +	if (!ret && mrq->data->error)
> +		ret = mrq->data->error;
> +	if (!ret && mrq->stop && mrq->stop->error)
> +		ret = mrq->stop->error;
> +	if (!ret && mrq->data->bytes_xfered !=
> +		mrq->data->blocks * mrq->data->blksz)
> +		ret = -EPERM;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mmc_check_result);
> +
> +/*
> ++ * transfer with certain parameters
> + */
> +int mmc_simple_transfer(struct mmc_card *card,
> +	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
> +	unsigned blocks, unsigned blksz, int write)
> +{
> +	struct mmc_request mrq = {0};
> +	struct mmc_command cmd = {0};
> +	struct mmc_command stop = {0};
> +	struct mmc_data data = {0};
> +
> +	mrq.cmd = &cmd;
> +	mrq.data = &data;
> +	mrq.stop = &stop;
> +
> +	mmc_prepare_mrq(card, &mrq, sg, sg_len, dev_addr,
> +		blocks, blksz, write);
> +
> +	mmc_wait_for_req(card->host, &mrq);
> +
> +	mmc_wait_busy(card);
> +
> +	return mmc_check_result(&mrq);
> +}
> +EXPORT_SYMBOL(mmc_simple_transfer);
>  
>  /**
>   * mmc_init_context_info() - init synchronization context
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index f206e29..92540d0 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -196,6 +196,14 @@ extern void mmc_put_card(struct mmc_card *card);
>  extern int mmc_flush_cache(struct mmc_card *);
>  
>  extern int mmc_detect_card_removed(struct mmc_host *host);
> +extern void mmc_prepare_mrq(struct mmc_card *card,
> +	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
> +	unsigned dev_addr, unsigned blocks, unsigned blksz, int write);
> +extern int mmc_wait_busy(struct mmc_card *card);
> +extern int mmc_check_result(struct mmc_request *mrq);
> +extern int mmc_simple_transfer(struct mmc_card *card,
> +	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
> +	unsigned blocks, unsigned blksz, int write);
>  
>  /**
>   *	mmc_claim_host - exclusively claim a host
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/3] mmc: Support FFU for eMMC v5.0
  2014-11-10 17:13 ` [PATCH 0/3] mmc: Support FFU for eMMC v5.0 Avi Shchislowski
@ 2014-11-11  3:33   ` Jaehoon Chung
  0 siblings, 0 replies; 88+ messages in thread
From: Jaehoon Chung @ 2014-11-11  3:33 UTC (permalink / raw)
  To: Avi Shchislowski, ulf.hansson
  Cc: 'Chris Ball', linux-mmc, Alex.Lemberg, gwendal, CPGS

Hi,

Subjects of Patch[1/3~3/3] are same, it can be confused.

Best Regards,
Jaehoon Chung

On 11/11/2014 02:13 AM, Avi Shchislowski wrote:
> he Field Firmware Update (FFU) feature is new for eMMC 5.0 spec (Jedec:
>  JESD84-B50.pdf)
>    
>  http://www.jedec.org/standards-documents/technology-focus-areas/flash-
>  memory-ssds-ufs-emmc/e-mmc
>  
>  *New ioctl has been add:
>  * [Alex Lemberg] "MMC_FFU_INVOKE - transfer the new Firmware data from user spac[Alex Lemberg]space
>  	to the eMMC deivce and install the new image[Alex Lemberg] firmware.
> * This solution allows to:
> - Complete eMMC 5.0 FFU procedure as an atomic operation, without being interrupted by other IO requests
> - Not limited Firmware data size. Using Multiple Write operations.
> - Support of both EXT_CSD_MODE_OPERATION_CODES modes [Alex Lemberg]
> * The solution is using "udev" device manager to transfer FW data from user space to eMMC driver [Alex Lemberg]
> * Pre-existing functions from mmc_test were used in this solution.
> 
> Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
> Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>
> 
> Avi Shchislowski (3):
>   mmc: Support FFU for eMMC v5.0
>   mmc: Support FFU for eMMC v5.0
>   mmc: Support FFU for eMMC v5.0
> 
>  drivers/mmc/card/Kconfig    |    8 +
>  drivers/mmc/card/block.c    |    5 +
>  drivers/mmc/card/mmc_test.c |   97 +--------
>  drivers/mmc/core/Makefile   |    1 +
>  drivers/mmc/core/core.c     |  128 ++++++++++++
>  drivers/mmc/core/mmc.c      |    9 +
>  drivers/mmc/core/mmc_ffu.c  |  487 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mmc/card.h    |    2 +
>  include/linux/mmc/core.h    |   30 +++
>  include/linux/mmc/mmc.h     |    9 +
>  10 files changed, 690 insertions(+), 86 deletions(-)
>  create mode 100644 drivers/mmc/core/mmc_ffu.c
> 


^ permalink raw reply	[flat|nested] 88+ messages in thread

* RE: [PATCH 1/3] mmc: Support FFU for eMMC v5.0
  2014-11-11  3:07   ` Jaehoon Chung
@ 2014-11-11  8:44     ` Avi Shchislowski
  0 siblings, 0 replies; 88+ messages in thread
From: Avi Shchislowski @ 2014-11-11  8:44 UTC (permalink / raw)
  To: Jaehoon Chung, ulf.hansson
  Cc: 'Chris Ball', linux-mmc, Alex Lemberg, gwendal, open list, CPGS


Hi 
I used the "git send-email..."

Thanks


>-----Original Message-----
>From: Jaehoon Chung [mailto:jh80.chung@samsung.com]
>Sent: Tuesday, November 11, 2014 5:08 AM
>To: Avi Shchislowski; ulf.hansson@linaro.org
>Cc: 'Chris Ball'; linux-mmc@vger.kernel.org; Alex Lemberg;
>gwendal@google.com; open list; CPGS
>Subject: Re: [PATCH 1/3] mmc: Support FFU for eMMC v5.0
>
>Hi,
>
>how did you make the patch?
>This patch didn't apply at Ulf's repository.
>
>
>On 11/11/2014 02:13 AM, Avi Shchislowski wrote:
>>
>> Add eMMC5.0 ffu ext_csd fields
>>
>> Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com>
>> Signed-off-by: Alex Lemberg <alex.lemberg@sandisk.com>
>>
>>
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index
>> 1eda8dd..3b86ed3 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -603,6 +603,15 @@ static int mmc_read_ext_csd(struct mmc_card
>*card, u8 *ext_csd)
>>  		card->ext_csd.data_sector_size = 512;
>>  	}
>>
>> +	/* eMMC v5 or later */
>> +	if (card->ext_csd.rev >= 7) {
>> +		card->ext_csd.ffu_capable =
>> +			((ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) ==
>0x1) &&
>> +			((ext_csd[EXT_CSD_FW_CONFIG] & 0x1) == 0x0);
>
>if (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1)
>&&  !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1))
>		card->ext_csd.ffu_capable = true;
>
>> +		card->ext_csd.ffu_mode_op =
>ext_csd[EXT_CSD_FFU_FEATURES];
>> +	} else {
>> +		card->ext_csd.ffu_capable = false;
>
>Can be set to false by default before checking whether card is eMMC v5.0 or
>not.
>
>card->ext_csd.ffu_capable = false;
>
>if (card->ext_csd.rev >= 7) {
>	...
>}
>
>Best Regards,
>Jaehoon Chung
>
>> +	}
>>  out:
>>  	return err;
>>  }
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index
>> d424b9d..dd01efe 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -81,6 +81,8 @@ struct mmc_ext_csd {
>>  	bool			hpi_en;			/* HPI
>enablebit */
>>  	bool			hpi;			/* HPI support bit */
>>  	unsigned int		hpi_cmd;		/* cmd used as HPI */
>> +	bool			ffu_capable;		/* FFU support */
>> +	bool			ffu_mode_op;		/* FFU mode
>operation */
>>  	bool			bkops;		/* background support bit */
>>  	bool			bkops_en;	/* background enable bit */
>>  	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index
>> 64ec963..787787b 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -272,6 +272,9 @@ struct _mmc_csd {
>>   * EXT_CSD fields
>>   */
>>
>> +#define EXT_CSD_FFU_STATUS		26	/* R */
>> +#define EXT_CSD_MODE_OPERATION_CODES	29	/* W */
>> +#define EXT_CSD_MODE_CONFIG		30	/* R/W */
>>  #define EXT_CSD_FLUSH_CACHE		32      /* W */
>>  #define EXT_CSD_CACHE_CTRL		33      /* R/W */
>>  #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
>> @@ -290,6 +293,7 @@ struct _mmc_csd {
>>  #define EXT_CSD_SANITIZE_START		165     /* W */
>>  #define EXT_CSD_WR_REL_PARAM		166	/* RO */
>>  #define EXT_CSD_RPMB_MULT		168	/* RO */
>> +#define EXT_CSD_FW_CONFIG		169	/* R/W */
>>  #define EXT_CSD_BOOT_WP			173	/* R/W */
>>  #define EXT_CSD_ERASE_GROUP_DEF		175	/* R/W */
>>  #define EXT_CSD_PART_CONFIG		179	/* R/W */
>> @@ -326,6 +330,11 @@ struct _mmc_csd {
>>  #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
>>  #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
>>  #define EXT_CSD_PWR_CL_DDR_200_360	253	/* RO */
>> +#define EXT_CSD_NUM_OF_FW_SEC_PROG	302	/* RO, 4 bytes
>*/
>> +#define EXT_CSD_FFU_ARG			487	/* RO, 4 bytes
>*/
>> +#define EXT_CSD_OPERATION_CODE_TIMEOUT	491	/* RO */
>> +#define EXT_CSD_FFU_FEATURES		492	/* RO */
>> +#define EXT_CSD_SUPPORTED_MODE		493	/* RO */
>>  #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
>>  #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
>>  #define EXT_CSD_MAX_PACKED_WRITES	500	/* RO */
>>


^ permalink raw reply	[flat|nested] 88+ messages in thread

end of thread, other threads:[~2014-11-11  8:44 UTC | newest]

Thread overview: 88+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <n@samsung.com>
2012-08-05 13:08 ` [RFC/PATCH] mmc: core: Add support for idle time BKOPs Maya Erez
2012-08-05 13:08   ` Maya Erez
2012-08-30  7:36   ` Jaehoon Chung
2012-09-04  5:42     ` merez
2012-09-04  5:42       ` merez
2012-10-04 22:28 ` [PATCH v2] mmc: core: Add support for idle time BKOPS Maya Erez
2012-10-04 22:28   ` Maya Erez
2012-10-09  3:52   ` merez
2012-10-09  3:52     ` merez
2012-10-16  7:53   ` Jaehoon Chung
2012-11-07  4:28     ` merez
2012-11-07  4:28       ` merez
2012-11-08  5:26       ` Jaehoon Chung
2012-11-11  9:25   ` Subhash Jadavani
2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
2012-10-18 16:31   ` [PATCH 2/6] mmc: core: sdio_bus.c: Removed trailing whitespace error Sangho Yi
2012-10-18 16:31   ` [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars Sangho Yi
2012-10-18 23:54     ` Jaehoon Chung
2012-10-18 16:31   ` [PATCH 4/6] mmc: core: sdio_bus.c: Removed space between func name and () Sangho Yi
2012-10-18 16:31   ` [PATCH 5/6] mmc: core: sdio_bus.c: Fixed a warning for the pr_warning( Sangho Yi
2012-10-18 16:31   ` [PATCH 6/6] mmc: core: sdio_bus.c: Removed unnecessary NULL check routine Sangho Yi
2012-10-18 16:47   ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Joe Perches
2012-11-01 14:40 ` [PATCH v2] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
2012-11-05  6:20   ` Per Förlin
2012-11-05  7:15     ` Jaehoon Chung
2012-11-12 12:10       ` Konstantin Dorfman
2012-11-08 10:41     ` Jaehoon Chung
2012-11-08 12:51       ` merez
2012-11-09  5:46         ` Jaehoon Chung
2012-11-12 12:49     ` Konstantin Dorfman
2012-11-06  8:40   ` Jaehoon Chung
2012-11-12 12:42     ` Konstantin Dorfman
2012-11-12 16:51 ` [PATCH v3] " Konstantin Dorfman
2012-11-13 13:42   ` Seungwon Jeon
2012-11-15 10:23     ` Seungwon Jeon
2012-11-20  2:05       ` Seungwon Jeon
2012-11-25 11:56 ` [PATCH v3] mmc: core: Add support for idle time BKOPS Maya Erez
2012-11-25 11:56   ` Maya Erez
2012-11-25 12:46   ` merez
2012-11-25 12:46     ` merez
2012-11-28 14:22     ` Chris Ball
2012-11-29 12:40   ` Jaehoon Chung
2012-12-03  9:49     ` merez
2012-12-04  9:52       ` Ulf Hansson
2012-12-04 21:17         ` merez
2012-12-06 10:18           ` Ulf Hansson
2012-12-12 12:32             ` merez
2012-12-13 10:17               ` Ulf Hansson
2012-12-21  8:35                 ` Maya Erez
2012-12-21  8:35                   ` Maya Erez
2012-12-21  9:56                   ` Ulf Hansson
2012-12-21 10:24                     ` Jaehoon Chung
2012-12-05 13:38 ` [PATCH v4] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
2012-12-06  5:24   ` Seungwon Jeon
2012-12-06 14:23     ` Konstantin Dorfman
2012-12-10 14:23 ` [RESEND PATCH " Konstantin Dorfman
2012-12-12  9:26   ` Seungwon Jeon
2012-12-17 12:26   ` Seungwon Jeon
2012-12-18 16:00     ` Konstantin Dorfman
2012-12-18 16:19       ` Chris Ball
2012-12-20  7:39       ` Seungwon Jeon
2012-12-26  9:26 ` [PATCH v5] " Konstantin Dorfman
2012-12-28 10:16   ` Seungwon Jeon
2013-01-14 19:31     ` Chris Ball
2013-01-15 12:00       ` Konstantin Dorfman
2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
2013-01-31 11:05     ` Jaehoon Chung
2013-01-31 11:25       ` Seungwon Jeon
2013-02-08 12:07     ` Konstantin Dorfman
2013-02-11 17:00     ` Chris Ball
2013-01-30  6:30   ` Seungwon Jeon
2013-01-31  6:53     ` Subhash Jadavani
2013-02-04 11:43     ` Subhash Jadavani
2013-02-05  5:57       ` Seungwon Jeon
2013-02-05  7:05         ` Jaehoon Chung
2013-02-05  7:32           ` Subhash Jadavani
2013-02-04 19:27     ` Konstantin Dorfman
2013-01-10 20:15 ` [PATCH v5 0/3] Add support for periodic BKOPS Maya Erez
2013-01-11 11:33   ` Jaehoon Chung
2014-11-10 17:13 ` [PATCH 0/3] mmc: Support FFU for eMMC v5.0 Avi Shchislowski
2014-11-11  3:33   ` Jaehoon Chung
2014-11-10 17:13 ` [PATCH 1/3] " Avi Shchislowski
2014-11-10 17:13   ` Avi Shchislowski
2014-11-11  3:07   ` Jaehoon Chung
2014-11-11  8:44     ` Avi Shchislowski
2014-11-10 17:18 ` [PATCH 2/3] " Avi Shchislowski
2014-11-10 17:18   ` Avi Shchislowski
2014-11-11  3:30   ` Jaehoon Chung

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.