Skip to contentSkip to conversation

mmc: Add support to handle Urgent data transfer request

PATCH New Konstantin Dorfman 1 participant 2 messages Jun 30, 2013

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 mmc: Add support to handle Urgent data transfer request

Motivation:

In order to decrease the latency of a prioritized request (such as READ requests) the device driver might decide to stop the transmission of a current "low priority" request in order to handle the "high priority" one. The urgency of the request is decided by the block layer I/O scheduler. When the block layer notifies the underlying device driver (eMMC for example) of an urgent request, the device driver might decide to stop the current request transmission. The remainder of the stopped request will be re-inserted back to the scheduler, to be re-scheduled after handling the urgent request.

This patch depends on following patches (in this order):
1. [PATCH v9 2/4] block: Add support for reinsert a dispatched req
2. [PATCH v9 1/4] block: make rq->cmd_flags be 64-bit
3. [PATCH v9 3/4] block: Add API for urgent request handling

In order to benefit in terms of read request latency I/O scheduler should implement urgent request classify and schedule policy, like one implemented by:
[PATCH v9 4/4] block: Adding ROW scheduling algorithm by Tanya Brokhman <[email protected]> [email protected]

These patches are introducing a new api's of the block layer, supporting its ability to classify incoming requests as urgent:
- blk_urgent_request() - set a notifier function, which will be called upon
urgent request arraiving at block layer
- blk_reinsert_request() - used to re-insert back request unserved by MMC requests

Besides block layer dependencies, following eMMC device and host controller capabilities needed:
- eMMC card HPI functionality to be able to interrupt undergoing write request
- stop_request() api implemented by host controller driver to correctly stop undergoing write transaction
- MMC_CAP2_STOP_REQUEST capability bit used to enable/disable the feature

The change extends existing MMC layer wait event functionality, that was introduced by following commit:

6035d97 mmc: fix async request mechanism for sequential read scenarios

Test was done with kernel 3.4 on msm platform. Latency measured using blktrace and custom instrumentation. Test was running parallel read/write lmdd stream. Latency is measured in ms. CFQ scheduler used as baseline, compared with ROW scheduler with the feature turned on.

Parallel lmdd:
./data/lmdd if=internal of=/data/write.dat bs=128k count=2500 sync=1 ./data/lmdd of=internal if=/data/readfile.dat bs=128k count=2500

        Throughput [Mb/sec]  Worst latency [msec]
        read     write       read     write
ROW     150      40          55       3800
CFQ     134      40          500      3280

Above is the average of 5 runs. Resulting worst case latency improved by factor 9.

Konstantin Dorfman (1):
mmc: Add support to handle Urgent data transfer request

 drivers/mmc/card/block.c |  151 +++++++++++++++++++++++++++++++++-
 drivers/mmc/card/queue.c |   54 ++++++++++++-
 drivers/mmc/card/queue.h |    5 +-
 drivers/mmc/core/core.c  |  208 ++++++++++++++++++++++++++++++++++++++++++++--
 include/linux/mmc/card.h |    5 +-
 include/linux/mmc/host.h |   16 ++++-
 include/linux/mmc/mmc.h  |    1 +
 7 files changed, 425 insertions(+), 15 deletions(-)
Signature · 6 lines
1.7.6
--
Konstantin Dorfman,
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center,
Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#1 lore raw

PATCH 1/1 mmc: Add support to handle Urgent data transfer request

Urgent request notification stops currently running transaction on bus.
In order to decrease a latency of a prioritized requests (aka Urgent request), we might want to stop the transmission of a running "low priority" request in order to handle the Urgent request. The urgency of the request is decided by the block layer I/O scheduler. When the block layer notifies the MMC layer of an urgent request and if the MMC layer is blocked on current request to complete, it will be woken up.

The decision on whether to stop an ongoing transfer is taken according to several parameters, one of them being the number of bytes already transferred for ongoing transfer by host controller so far.

To calculate how many bytes were successfully programmed before stop, CORRECTLY_PRG_SECTORS_NUM[245:242] parsed from EXT_CSD register. The remainder of stopped request (and next prepared request in case it exists) re-inserted back to the I/O scheduler to be handled after the completion of the urgent request.

In case it is decided not to stop the ongoing transfer, MMC context will wait for normal completion of the ongoing transfer, then already prepared next request (if it exists) will be re-inserted back into block layer and the urgent request fetched.

URGENT_REQUEST handling has following dependencies:
1. Host controller driver should support mmc_host op named "stop_request"
2. Block I/O scheduler should support re-insert API
3. eMMC card should support HPI (High Priority Interrupt) command

If any of the above dependencies are not met then urgent request mechanism will not become operational.

Change-Id Ic3fa1ca9463cc8991aefee940d8bfddf76c111d3
drivers/mmc/card/block.c +150 -1
index cd0b7f4..da45dc5 100644
@@ -1203,6 +1203,122 @@ static int mmc_blk_err_check(struct mmc_card *card,
 	return MMC_BLK_SUCCESS;
 }
 
+/*
+ * mmc_blk_reinsert_req() - re-insert request back to the scheduler
+ * @areq:	request to re-insert.
+ *
+ * Request may be packed or single. When fails to reinsert request, it will be
+ * requeued to the the dispatch queue.
+ */
+static void mmc_blk_reinsert_req(struct mmc_async_req *areq)
+{
+	struct request *prq;
+	int ret = 0;
+	struct mmc_queue_req *mq_rq;
+	struct request_queue *q;
+
+	mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
+	q = mq_rq->req->q;
+	if (mq_rq->cmd_type != MMC_PACKED_NONE) {
+		while (!list_empty(&mq_rq->packed->list)) {
+			/* return requests in reverse order */
+			prq = list_entry_rq(mq_rq->packed->list.prev);
+			list_del_init(&prq->queuelist);
+			spin_lock_irq(q->queue_lock);
+			ret = blk_reinsert_request(q, prq);
+			if (ret) {
+				blk_requeue_request(q, prq);
+				spin_unlock_irq(q->queue_lock);
+				goto reinsert_error;
+			}
+			spin_unlock_irq(q->queue_lock);
+		}
+	} else {
+		spin_lock_irq(q->queue_lock);
+		ret = blk_reinsert_request(q, mq_rq->req);
+		if (ret)
+			blk_requeue_request(q, mq_rq->req);
+		spin_unlock_irq(q->queue_lock);
+	}
+	return;
+
+reinsert_error:
+	pr_err("%s: blk_reinsert_request() failed (%d)",
+			mq_rq->req->rq_disk->disk_name, ret);
+	/*
+	 * -EIO will be reported for this request and rest of packed->list.
+	 *  Urgent request will be proceeded anyway, while upper layer
+	 *  responsibility to re-send failed requests
+	 */
+	while (!list_empty(&mq_rq->packed->list)) {
+		prq = list_entry_rq(mq_rq->packed->list.next);
+		list_del_init(&prq->queuelist);
+		spin_lock_irq(q->queue_lock);
+		blk_requeue_request(q, prq);
+		spin_unlock_irq(q->queue_lock);
+	}
+}
+
+/*
+ * mmc_blk_update_interrupted_req() - update of the stopped request
+ * @card:	the MMC card associated with the request.
+ * @areq:	interrupted async request.
+ *
+ * Get stopped request state from card and update successfully done part of
+ * the request by setting packed_fail_idx.  The packed_fail_idx is index of
+ * first uncompleted request in packed request list, for non-packed request
+ * packed_fail_idx remains unchanged.
+ *
+ * Returns: MMC_BLK_SUCCESS for success, MMC_BLK_ABORT otherwise
+ */
+static int mmc_blk_update_interrupted_req(struct mmc_card *card,
+					struct mmc_async_req *areq)
+{
+	int ret = MMC_BLK_SUCCESS;
+	u8 *ext_csd;
+	int correctly_done;
+	struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
+				      mmc_active);
+	struct request *prq;
+	u8 req_index = 0;
+	struct mmc_packed *packed;
+
+	if (mq_rq->cmd_type == MMC_PACKED_NONE)
+		return MMC_BLK_SUCCESS;
+
+	ext_csd = kmalloc(512, GFP_KERNEL);
+	if (!ext_csd)
+		return MMC_BLK_ABORT;
+
+	/* get correctly programmed sectors number from card */
+	ret = mmc_send_ext_csd(card, ext_csd);
+	if (ret) {
+		pr_err("%s: error %d reading ext_csd\n",
+				mmc_hostname(card->host), ret);
+		ret = MMC_BLK_ABORT;
+		goto exit;
+	}
+	correctly_done = card->ext_csd.data_sector_size *
+		(ext_csd[EXT_CSD_CORRECTLY_PRG_SECTORS_NUM + 0] << 0 |
+		 ext_csd[EXT_CSD_CORRECTLY_PRG_SECTORS_NUM + 1] << 8 |
+		 ext_csd[EXT_CSD_CORRECTLY_PRG_SECTORS_NUM + 2] << 16 |
+		 ext_csd[EXT_CSD_CORRECTLY_PRG_SECTORS_NUM + 3] << 24);
+
+	packed = mq_rq->packed;
+	list_for_each_entry(prq, &packed->list, queuelist) {
+		if ((correctly_done - (int)blk_rq_bytes(prq)) < 0) {
+			/* prq is not successfull */
+			packed->idx_failure = req_index;
+			break;
+		}
+		correctly_done -= blk_rq_bytes(prq);
+		req_index++;
+	}
+exit:
+	kfree(ext_csd);
+	return ret;
+}
+
 static int mmc_blk_packed_err_check(struct mmc_card *card,
 				    struct mmc_async_req *areq)
 {
@@ -1411,7 +1527,11 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
 	}
 
 	mqrq->mmc_active.mrq = &brq->mrq;
+	mqrq->mmc_active.cmd_flags = req->cmd_flags;
 	mqrq->mmc_active.err_check = mmc_blk_err_check;
+	mqrq->mmc_active.reinsert_req = mmc_blk_reinsert_req;
+	mqrq->mmc_active.update_interrupted_req =
+		mmc_blk_update_interrupted_req;
 
 	mmc_queue_bounce_pre(mqrq);
 }
@@ -1620,6 +1740,11 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
 
 	mqrq->mmc_active.mrq = &brq->mrq;
 	mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+	mqrq->mmc_active.cmd_flags = req->cmd_flags;
+
+	mqrq->mmc_active.reinsert_req = mmc_blk_reinsert_req;
+	mqrq->mmc_active.update_interrupted_req =
+		mmc_blk_update_interrupted_req;
 
 	mmc_queue_bounce_pre(mqrq);
 }
@@ -1780,6 +1905,20 @@ 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_URGENT:
+			if (mq_rq->cmd_type != MMC_PACKED_NONE) {
+				/* complete successfully transmitted part */
+				if (mmc_blk_end_packed_req(mq_rq))
+					/* process for not transmitted part */
+					mmc_blk_reinsert_req(areq);
+			} else {
+				mmc_blk_reinsert_req(areq);
+			}
+
+			mq->flags |= MMC_QUEUE_URGENT_REQUEST;
+			ret = 0;
+			break;
+		case MMC_BLK_URGENT_DONE:
 		case MMC_BLK_SUCCESS:
 		case MMC_BLK_PARTIAL:
 			/*
@@ -1959,13 +2098,23 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 	}
 
 out:
+	/*
+	 * packet burst is over, when one of the following occurs:
+	 * - no more requests and new request notification is not in progress
+	 * - urgent notification in progress and current request is not urgent
+	 *   (all existing requests completed or reinserted to the block layer)
+	 */
 	if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
-	     (req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK)))
+	     (req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK)) ||
+			((mq->flags & MMC_QUEUE_URGENT_REQUEST) &&
+				!(mq->mqrq_cur->req->cmd_flags & REQ_URGENT)))
+
 		/*
 		 * Release host when there are no more requests
 		 * and after special request(discard, flush) is done.
 		 * In case sepecial request, there is no reentry to
 		 * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
+		 * In case urgent request is pending, no release host
 		 */
 		mmc_put_card(card);
 	return ret;
drivers/mmc/card/queue.c +53 -1

79 lines not on this page · raw message

drivers/mmc/card/queue.h +3 -2
index 5752d50..e8bb320 100644
@@ -48,8 +48,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)
+#define MMC_QUEUE_SUSPENDED		(1 << 0)
+#define MMC_QUEUE_NEW_REQUEST		(1 << 1)
+#define MMC_QUEUE_URGENT_REQUEST	(1 << 2)
 
 	int			(*issue_fn)(struct mmc_queue *, struct request *);
 	void			*data;
drivers/mmc/core/core.c +199 -9

268 lines not on this page · raw message

include/linux/mmc/card.h +4 -1
index 842de3e..bbd4ede 100644
@@ -196,6 +196,7 @@ struct sdio_cis {
 struct mmc_host;
 struct sdio_func;
 struct sdio_func_tuple;
+struct mmc_queue;
 
 #define SDIO_MAX_FUNCS		7
 
@@ -209,6 +210,8 @@ enum mmc_blk_status {
 	MMC_BLK_ECC_ERR,
 	MMC_BLK_NOMEDIUM,
 	MMC_BLK_NEW_REQUEST,
+	MMC_BLK_URGENT,
+	MMC_BLK_URGENT_DONE,
 };
 
 /* The number of MMC physical partitions.  These consist of:
@@ -528,5 +531,5 @@ extern void mmc_unregister_driver(struct mmc_driver *);
 
 extern void mmc_fixup_device(struct mmc_card *card,
 			     const struct mmc_fixup *table);
-
+extern void mmc_blk_disable_wr_packing(struct mmc_queue *mq);
 #endif /* LINUX_MMC_CARD_H */
include/linux/mmc/host.h +15 -1
index 3b0c33a..4617036 100644
@@ -139,6 +139,8 @@ struct mmc_host_ops {
 	int	(*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
 	void	(*hw_reset)(struct mmc_host *host);
 	void	(*card_event)(struct mmc_host *host);
+	int	(*stop_request)(struct mmc_host *host);
+	unsigned int	(*get_xfer_remain)(struct mmc_host *host);
 };
 
 struct mmc_card;
@@ -147,11 +149,18 @@ struct device;
 struct mmc_async_req {
 	/* active mmc request */
 	struct mmc_request	*mrq;
+	unsigned int cmd_flags; /* copied from struct request */
+
 	/*
 	 * Check error status of completed mmc request.
 	 * Returns 0 if success otherwise non zero.
 	 */
 	int (*err_check) (struct mmc_card *, struct mmc_async_req *);
+	/* Reinserts request back to the block layer */
+	void (*reinsert_req) (struct mmc_async_req *);
+	/* update what part of request is not done (packed_fail_idx) */
+	int (*update_interrupted_req) (struct mmc_card *,
+			struct mmc_async_req *);
 };
 
 /**
@@ -176,7 +185,10 @@ struct mmc_slot {
  * 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
+ * @is_waiting_last_req	is true, when 1 request running on the bus and
+ *			NULL fetched as second request. MMC_BLK_NEW_REQUEST
+ *			notification will wake up mmc thread from waiting.
+ * @is_urgent		wake up reason was urgent request
  * @wait		wait queue
  * @lock		lock to protect data fields
  */
@@ -184,6 +196,7 @@ struct mmc_context_info {
 	bool			is_done_rcv;
 	bool			is_new_req;
 	bool			is_waiting_last_req;
+	bool			is_urgent;
 	wait_queue_head_t	wait;
 	spinlock_t		lock;
 };
@@ -282,6 +295,7 @@ struct mmc_host {
 #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14)	/* Don't power up before scan */
 #define MMC_CAP2_SANITIZE	(1 << 15)		/* Support Sanitize */
 
+#define MMC_CAP2_STOP_REQUEST	(1 << 14)	/* Allow stop ongoing request */
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
 #ifdef CONFIG_MMC_CLKGATE
include/linux/mmc/mmc.h +1 -0
index 50bcde3..38014cf 100644
@@ -321,6 +321,7 @@ struct _mmc_csd {
 #define EXT_CSD_PWR_CL_200_360		237	/* RO */
 #define EXT_CSD_PWR_CL_DDR_52_195	238	/* RO */
 #define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
+#define EXT_CSD_CORRECTLY_PRG_SECTORS_NUM 242	/* RO, 4 bytes */
 #define EXT_CSD_BKOPS_STATUS		246	/* RO */
 #define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
 #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
Signature · 1 line
1.7.6