summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRitesh Harjani <riteshh@codeaurora.org>2016-02-15 14:20:21 +0530
committerSubhash Jadavani <subhashj@codeaurora.org>2016-05-31 15:28:12 -0700
commit284f0655519f1a81a470bb5df396b151b207b6a4 (patch)
treebe562d0282e278e9843a2e953deccefa6cdd4314
parentcc6df31db6b39446b338b61932b0fc8f614baf89 (diff)
mmc: queue: Fix queue_lock spinlock bug from CMDQ shutdown path
CMDQ shutdown path calls blk_cleanup_queue, which changes queue_lock from driver lock to it's original request_queue lock. Hence during above shutdown process if below sequence is exercised as well then may see below spinlock bug. a) Some process say iozoneA has already acquired queue_lock (which is md->lock). b) adb reboot has been issued and CMDQ driver has completed calling blk_cleanup_queue which switches the queue_lock from md->lock to q->__queue_lock. c) ProcessA tries to release queue_lock but finds an unbalance that the lock is already released Hence remove blk_cleanup_queue and instead make sure there are no active_reqs in flight by mmccmdqd before this kthread is exited. Callstack: <6> BUG: spinlock already unlocked on CPU#6, iozone/4391 <6> lock: 0xffffffc06ab8be80, .magic: dead4ead, .owner: <none>/-1, .owner_cpu: -1 [ffffffc0420e3b28] __delay at ffffffc00031a328 [ffffffc0420e3b38] __const_udelay at ffffffc00031a304 [ffffffc0420e3b58] msm_trigger_wdog_bite at ffffffc0004476cc [ffffffc0420e3b68] spin_bug at ffffffc0000e4554 [ffffffc0420e3b98] do_raw_spin_unlock at ffffffc0000e47a0 [ffffffc0420e3bc8] _raw_spin_unlock_irq at ffffffc000db3ee0 [ffffffc0420e3be8] blk_queue_bio at ffffffc0002ff1e4 [ffffffc0420e3bf8] generic_make_request at ffffffc0002fd210 [ffffffc0420e3c58] submit_bio at ffffffc0002fd328 [ffffffc0420e3ca8] submit_bio_wait at ffffffc0002f5768 [ffffffc0420e3d00] compat_sys_call_table at ffffffc00008e000 [ffffffc0420e3d18] submit_bio_wait at ffffffc0002f574c [ffffffc0420e3d38] __blkdev_issue_flush at ffffffc00030043c [ffffffc0420e3da8] blkdev_issue_flush at ffffffc000300494 [ffffffc0420e3dd8] ext4_sync_fs at ffffffc0002597a4 CRs-fixed: 953541 Change-Id: I769cc25c14b6d873f64a898d6b73f33cc59d9c5d Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
-rw-r--r--drivers/mmc/card/block.c3
-rw-r--r--drivers/mmc/card/queue.c25
-rw-r--r--drivers/mmc/card/queue.h1
3 files changed, 26 insertions, 3 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 02f2d323704e..97fcd1cf82b6 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3307,6 +3307,9 @@ 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;
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 5c90d46fb65d..75a51bd2fc0b 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -97,7 +97,8 @@ static inline void mmc_cmdq_ready_wait(struct mmc_host *host,
* 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) &&
+ wait_event(ctx->wait, kthread_should_stop()
+ || (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)
@@ -105,7 +106,7 @@ static inline void mmc_cmdq_ready_wait(struct mmc_host *host,
&& !(!host->card->part_curr && mmc_host_cq_disable(host) &&
!mmc_card_suspended(host->card))
&& !test_bit(CMDQ_STATE_ERR, &ctx->curr_state)
- && !mmc_check_blk_queue_start_tag(q, mq->cmdq_req_peeked));
+ && !mmc_check_blk_queue_start_tag(q, mq->cmdq_req_peeked)));
}
static int mmc_cmdq_thread(void *d)
@@ -122,6 +123,8 @@ static int mmc_cmdq_thread(void *d)
int ret = 0;
mmc_cmdq_ready_wait(host, mq);
+ if (kthread_should_stop())
+ break;
ret = mq->cmdq_issue_fn(mq, mq->cmdq_req_peeked);
/*
@@ -662,6 +665,7 @@ 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);
@@ -718,7 +722,22 @@ int mmc_queue_suspend(struct mmc_queue *mq, int wait)
goto out;
if (wait) {
- blk_cleanup_queue(q);
+
+ /*
+ * After blk_stop_queue is called, wait for all
+ * active_reqs to complete.
+ * Then wait for cmdq thread to exit before calling
+ * cmdq shutdown to avoid race between issuing
+ * requests and shutdown of cmdq.
+ */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (host->cmdq_ctx.active_reqs)
+ wait_for_completion(
+ &mq->cmdq_shutdown_complete);
+ kthread_stop(mq->thread);
mq->cmdq_shutdown(mq);
} else {
spin_lock_irqsave(q->queue_lock, flags);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 964262f0eb79..d8a33ac55c91 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -72,6 +72,7 @@ struct mmc_queue {
struct work_struct cmdq_err_work;
struct completion cmdq_pending_req_done;
+ struct completion cmdq_shutdown_complete;
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 *);