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

Commit 3eb69524 authored by Subhash Jadavani's avatar Subhash Jadavani
Browse files

mmc: card: fix race with shutdown handler



There could be 2 different race conditions possible with current shutdown
handler:
1. Shutdown handler sees that there is no request in queue but mmc-cmdqd
   might have fetched the request from queue and about to issue it to
   driver.
2. Shutdown handler keeps looping in the while(1) loop as it sees request
   being pending in the request queue. But mmc-cmdqd thread doesn't wake up
   to fetch the requests from the request queue. mmc-cmdqd thread isn't
   waking up because shutdown path has stopped queue and request came into
   the queue after that. Once queue is stopped, block layer won't invoke
   the queue's ->request_fn() callback to notify driver about pending
   request.

Remedy to fix both these race condition is simple. In shutdown handler,
make sure that we drain (& complete) all the outstanding requests from the
queue and then don't allow any new requests to be queued. Block layer API
blk_cleanup_queue() precisely does what we want and this change basically
use the same API in shutdown handler.

Change-Id: I761ba6a2e2974d955bb72ff993b1cc2c32c9ec29
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
parent bb29ea15
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -3115,8 +3115,6 @@ out:
	if (!ctx_info->active_reqs)
		wake_up_interruptible(&host->cmdq_ctx.queue_empty_wq);

	if (blk_queue_stopped(mq->queue) && !ctx_info->active_reqs)
		complete(&mq->cmdq_shutdown_complete);
	return;
}

+6 −31
Original line number Diff line number Diff line
@@ -649,7 +649,6 @@ int mmc_cmdq_init(struct mmc_queue *mq, struct mmc_card *card)

	blk_queue_softirq_done(mq->queue, mmc_cmdq_softirq_done);
	INIT_WORK(&mq->cmdq_err_work, mmc_cmdq_error_work);
	init_completion(&mq->cmdq_shutdown_complete);
	init_completion(&mq->cmdq_pending_req_done);

	blk_queue_rq_timed_out(mq->queue, mmc_cmdq_rq_timed_out);
@@ -698,7 +697,6 @@ int mmc_queue_suspend(struct mmc_queue *mq, int wait)
	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;
@@ -706,44 +704,21 @@ int mmc_queue_suspend(struct mmc_queue *mq, int wait)
		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) {
			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);

			blk_cleanup_queue(q);
			mq->cmdq_shutdown(mq);
		} else {
			spin_lock_irqsave(q->queue_lock, flags);
			blk_stop_queue(q);
			wake_up(&host->cmdq_ctx.wait);
			req = blk_peek_request(q);
			spin_unlock_irqrestore(q->queue_lock, flags);

			if (req || host->cmdq_ctx.active_reqs) {
			if (req || mq->cmdq_req_peeked ||
			    host->cmdq_ctx.active_reqs) {
				clear_bit(MMC_QUEUE_SUSPENDED, &mq->flags);
				spin_lock_irqsave(q->queue_lock, flags);
				blk_start_queue(q);
				spin_unlock_irqrestore(q->queue_lock, flags);
				rc = -EBUSY;
			}
			spin_unlock_irqrestore(q->queue_lock, flags);
		}

		goto out;
+0 −1
Original line number Diff line number Diff line
@@ -69,7 +69,6 @@ struct mmc_queue {
	int			num_wr_reqs_to_start_packing;
	bool			no_pack_for_random;
	struct work_struct	cmdq_err_work;
	struct completion	cmdq_shutdown_complete;

	struct completion	cmdq_pending_req_done;
	struct request		*cmdq_req_peeked;