Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cf4615c authored by Subhash Jadavani's avatar Subhash Jadavani Committed by Venkat Gopalakrishnan
Browse files

mmc: queue: fix the cmdq thread wake up handling



If request has to be requeued (due to any DCMD commmand pending or cmdq
being halted) and if we change the task status to interruptible before
going to sleep then thread may not wakeup again. Note that
blk_requeue_request() doesn't trigger ->request_fn() again to wakeup
the thread.

Fix this issue by making cmdq thread wait for the completion of DCMD
or until the cmdq is unhalted. This change also simplifies the
cmdq thread function.

Change-Id: Iebffc993241e5fadb2962fedc44576566dc66e9c
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: default avatarVenkat Gopalakrishnan <venkatg@codeaurora.org>
parent 50e0173f
Loading
Loading
Loading
Loading
+6 −12
Original line number Diff line number Diff line
@@ -1548,9 +1548,7 @@ clear_dcmd:
	}
out:
	blk_end_request(req, err, blk_rq_bytes(req));

	if (test_and_clear_bit(0, &ctx_info->req_starved))
		blk_run_queue(mq->queue);
	wake_up(&ctx_info->wait);
	mmc_put_card(card);
	return err ? 1 : 0;
}
@@ -1664,9 +1662,7 @@ clear_dcmd:
	}
out:
	blk_end_request(req, err, blk_rq_bytes(req));

	if (test_and_clear_bit(0, &ctx_info->req_starved))
		blk_run_queue(mq->queue);
	wake_up(&ctx_info->wait);
	mmc_put_card(card);
	return err ? 1 : 0;
}
@@ -3052,10 +3048,9 @@ unhalt:
out:
	host->err_mrq = NULL;
	pm_runtime_mark_last_busy(&card->dev);
	clear_bit(CMDQ_STATE_ERR, &ctx_info->curr_state);
	wake_up(&ctx_info->wait);
	__mmc_put_card(card);

	if (test_and_clear_bit(0, &ctx_info->req_starved))
		blk_run_queue(mrq->req->q);
}

/* invoked by block layer in softirq context */
@@ -3111,9 +3106,8 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
out:

	mmc_cmdq_clk_scaling_stop_busy(host, true, is_dcmd);
	if (!test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state) &&
			test_and_clear_bit(0, &ctx_info->req_starved))
		blk_run_queue(mq->queue);
	if (!test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state))
		wake_up(&ctx_info->wait);

	mmc_put_card(host->card);
	if (!ctx_info->active_reqs)
+90 −78
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include <linux/delay.h>

#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -54,91 +55,81 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
	return BLKPREP_OK;
}

static inline bool mmc_cmdq_should_pull_reqs(struct mmc_host *host,
					struct mmc_cmdq_context_info *ctx,
static struct request *mmc_peek_request(struct mmc_queue *mq)
{
	struct request_queue *q = mq->queue;

	spin_lock_irq(q->queue_lock);
	mq->cmdq_req_peeked = blk_peek_request(q);
	spin_unlock_irq(q->queue_lock);

	return mq->cmdq_req_peeked;
}

static bool mmc_check_blk_queue_start_tag(struct request_queue *q,
					  struct request *req)
{
	int ret;

	spin_lock_irq(q->queue_lock);
	ret = blk_queue_start_tag(q, req);
	spin_unlock_irq(q->queue_lock);

	return !!ret;
}

static inline void mmc_cmdq_ready_wait(struct mmc_host *host,
					struct mmc_queue *mq)
{
	bool ret = true;

	if ((req->cmd_flags & (REQ_FLUSH | REQ_DISCARD)) &&
			test_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx->curr_state))
		ret = false;
	else if (!host->card->part_curr &&
			mmc_host_halt(host) && !mmc_card_suspended(host->card))
		ret = false;
	else if (test_bit(CMDQ_STATE_ERR, &ctx->curr_state))
		ret = false;

	if (!ret)
		pr_debug("%s: %s: skip pulling reqs: state: %lu, cmd_flags: 0x%x\n",
			 mmc_hostname(host), __func__,
			 ctx->curr_state, (unsigned int)req->cmd_flags);
	return ret;
	struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
	struct request_queue *q = mq->queue;

	/*
	 * Wait until all of the following conditions are true:
	 * 1. There is a request pending in the block layer queue
	 *    to be processed.
	 * 2. If the peeked request is flush/discard then there shouldn't
	 *    be any other direct command active.
	 * 3. cmdq state should be unhalted.
	 * 4. cmdq state shouldn't be in error state.
	 * 5. free tag available to process the new request.
	 */
	wait_event(ctx->wait, mmc_peek_request(mq) &&
		!((mq->cmdq_req_peeked->cmd_flags & (REQ_FLUSH | REQ_DISCARD))
		  && test_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx->curr_state))
		&& !(!host->card->part_curr && !mmc_card_suspended(host->card)
		     && mmc_host_halt(host))
		&& !test_bit(CMDQ_STATE_ERR, &ctx->curr_state)
		&& !mmc_check_blk_queue_start_tag(q, mq->cmdq_req_peeked));
}

static int mmc_cmdq_thread(void *d)
{
	struct mmc_queue *mq = d;
	struct request_queue *q = mq->queue;
	struct mmc_card *card = mq->card;

	struct request *req;
	struct mmc_host *host = card->host;
	struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
	unsigned long flags;

	current->flags |= PF_MEMALLOC;
	if (card->host->wakeup_on_idle)
		set_wake_up_idle(true);

	down(&mq->thread_sem);
	while (1) {
		int ret = 0;

		spin_lock_irqsave(q->queue_lock, flags);
		set_current_state(TASK_INTERRUPTIBLE);
		req = blk_peek_request(q);
		if (req) {
			ret = blk_queue_start_tag(q, req);
			spin_unlock_irqrestore(q->queue_lock, flags);
			if (ret) {
				test_and_set_bit(0, &ctx->req_starved);
				schedule();
			} else {
				if (!mmc_cmdq_should_pull_reqs(host, ctx,
							       req)) {
					spin_lock_irqsave(q->queue_lock, flags);
					blk_requeue_request(q, req);
					spin_unlock_irqrestore(q->queue_lock,
							       flags);
					test_and_set_bit(0, &ctx->req_starved);
					schedule();
					continue;
				}
				set_current_state(TASK_RUNNING);
				ret = mq->cmdq_issue_fn(mq, req);
				if (ret) {
					pr_err("%s: failed (%d) to issue req, requeue\n",
					       mmc_hostname(host), ret);
					spin_lock_irqsave(q->queue_lock, flags);
					blk_requeue_request(q, req);
					spin_unlock_irqrestore(q->queue_lock,
							       flags);
				}
			}
		} else {
			spin_unlock_irqrestore(q->queue_lock, flags);
			if (kthread_should_stop()) {
				set_current_state(TASK_RUNNING);
				break;
			}
			up(&mq->thread_sem);
			schedule();
			down(&mq->thread_sem);
		}
		mmc_cmdq_ready_wait(host, mq);

		ret = mq->cmdq_issue_fn(mq, mq->cmdq_req_peeked);
		/*
		 * Don't requeue if issue_fn fails, just bug on.
		 * We don't expect failure here and there is no recovery other
		 * than fixing the actual issue if there is any.
		 * Also we end the request if there is a partition switch error,
		 * so we should not requeue the request here.
		 */
		if (ret)
			BUG_ON(1);
	} /* loop */
	up(&mq->thread_sem);

	return 0;
}

@@ -207,7 +198,7 @@ static void mmc_cmdq_dispatch_req(struct request_queue *q)
{
	struct mmc_queue *mq = q->queuedata;

	wake_up_process(mq->thread);
	wake_up(&mq->card->host->cmdq_ctx.wait);
}

/*
@@ -346,7 +337,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
			if (card->host->cmdq_ops->init)
				card->host->cmdq_ops->init(card->host);
			mq->queue->queuedata = mq;
			card->host->cmdq_ctx.q = mq->queue;
			mq->thread = kthread_run(mmc_cmdq_thread, mq,
						 "mmc-cmdqd/%d%s",
						 host->index,
@@ -628,6 +618,8 @@ int mmc_cmdq_init(struct mmc_queue *mq, struct mmc_card *card)
	}

	init_waitqueue_head(&card->host->cmdq_ctx.queue_empty_wq);
	init_waitqueue_head(&card->host->cmdq_ctx.wait);

	mq->mqrq_cmdq = kzalloc(
			sizeof(struct mmc_queue_req) * q_depth, GFP_KERNEL);
	if (!mq->mqrq_cmdq) {
@@ -705,29 +697,47 @@ int mmc_queue_suspend(struct mmc_queue *mq, int wait)
	unsigned long flags;
	int rc = 0;
	struct mmc_card *card = mq->card;
	struct request *req;
	#define SLEEP_TIME_BETWEEN_BLK_REQ_CHECK	100 /* microseconds */

	if (card->cmdq_init && blk_queue_tagged(q)) {
		struct mmc_host *host = card->host;

		if (test_and_set_bit(MMC_QUEUE_SUSPENDED, &mq->flags))
			goto out;

		spin_lock_irqsave(q->queue_lock, flags);
		blk_stop_queue(q);
		spin_unlock_irqrestore(q->queue_lock, flags);

		wake_up(&host->cmdq_ctx.wait);

		if (wait) {
			/*
			 * Wait for already queued requests to be issued by
			 * mmc_cmdqd.
			 */
			down(&mq->thread_sem);
			while (1) {
				spin_lock_irqsave(q->queue_lock, flags);
				req = blk_peek_request(q);
				spin_unlock_irqrestore(q->queue_lock, flags);

				if (!req)
					break;

				/* sleep for some time before rechecking */
				usleep_range(SLEEP_TIME_BETWEEN_BLK_REQ_CHECK,
					SLEEP_TIME_BETWEEN_BLK_REQ_CHECK + 10);
			}

			/* Wait for already issued requests to complete */
			if (host->cmdq_ctx.active_reqs)
				wait_for_completion(
						&mq->cmdq_shutdown_complete);

			mq->cmdq_shutdown(mq);
		} else if (!test_and_set_bit(MMC_QUEUE_SUSPENDED, &mq->flags)) {
			rc = down_trylock(&mq->thread_sem);
			if (rc || host->cmdq_ctx.active_reqs) {
		} else {
			spin_lock_irqsave(q->queue_lock, flags);
			req = blk_peek_request(q);
			spin_unlock_irqrestore(q->queue_lock, flags);

			if (req || host->cmdq_ctx.active_reqs) {
				clear_bit(MMC_QUEUE_SUSPENDED, &mq->flags);
				spin_lock_irqsave(q->queue_lock, flags);
				blk_start_queue(q);
@@ -771,10 +781,12 @@ out:
void mmc_queue_resume(struct mmc_queue *mq)
{
	struct request_queue *q = mq->queue;
	struct mmc_card *card = mq->card;
	unsigned long flags;

	if (test_and_clear_bit(MMC_QUEUE_SUSPENDED, &mq->flags)) {

		if (!(card->cmdq_init && blk_queue_tagged(q)))
			up(&mq->thread_sem);

		spin_lock_irqsave(q->queue_lock, flags);
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ struct mmc_queue {
	struct completion	cmdq_shutdown_complete;

	struct completion	cmdq_pending_req_done;
	struct request		*cmdq_req_peeked;
	int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
	void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
	void (*cmdq_shutdown)(struct mmc_queue *);
+1 −3
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/jiffies.h>
#include <linux/blkdev.h>

#include <trace/events/mmc.h>

@@ -1483,8 +1482,7 @@ int mmc_cmdq_halt(struct mmc_host *host, bool halt)
			mmc_host_set_halt(host);
		else if (!err && !halt) {
			mmc_host_clr_halt(host);
			if (host->cmdq_ctx.q)
				blk_run_queue(host->cmdq_ctx.q);
			wake_up(&host->cmdq_ctx.wait);
		}
	} else {
		err = -ENOSYS;
+4 −5
Original line number Diff line number Diff line
@@ -241,11 +241,12 @@ struct mmc_slot {
 * @active_reqs		requests being processed
 * @data_active_reqs	data requests being processed
 * @curr_state		state of cmdq engine
 * @req_starved		completion should invoke the request_fn since
 *			no tags were available
 * @cmdq_ctx_lock	acquire this before accessing this structure
 * @queue_empty_wq	workqueue for waiting for all
 *			the outstanding requests to be completed
 * @wait		waiting for all conditions described in
 *			mmc_cmdq_ready_wait to be satisified before
 *			issuing the new request to LLD.
 */
struct mmc_cmdq_context_info {
	unsigned long	active_reqs; /* in-flight requests */
@@ -254,10 +255,8 @@ struct mmc_cmdq_context_info {
#define	CMDQ_STATE_ERR 0
#define	CMDQ_STATE_DCMD_ACTIVE 1
#define	CMDQ_STATE_HALT 2
	/* no free tag available */
	unsigned long	req_starved;
	wait_queue_head_t	queue_empty_wq;
	struct request_queue *q;
	wait_queue_head_t	wait;
};

/**