Re: [RESEND PATCH v4] mmc: fix async request mechanism for sequential read scenarios

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxx>
>>> ---
>>>
>>>
>>> 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@xxxxxxxxxxxxxxx
>>> 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
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux