aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/dw_mmc.c
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2011-06-24 08:55:10 -0400
committerChris Ball <cjb@laptop.org>2011-07-20 17:20:58 -0400
commit7456caae37396fc1bc6f8e9461d07664b8c2f280 (patch)
tree972f231756e8e766aa367689085a2834c7f6fcb4 /drivers/mmc/host/dw_mmc.c
parentb40af3aa7712e8f1b73e00e781cd93181483f649 (diff)
mmc: dw_mmc: fix race with request and removal
When a request is made, the card presence is checked and the request is queued. These two parts must be atomic with respect to card removal, or a card removal could be handled in between, and the new request wouldn't get cancelled until another card was inserted. Therefore move the spinlock protection from dw_mci_queue_request() up into dw_mci_request() to cover the presence check. Note that the test_bit() used for the presence check isn't atomic itself, so should have been protected by a spinlock anyway. Signed-off-by: James Hogan <james.hogan@imgtec.com> Acked-by: Will Newton <will.newton@imgtec.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
-rw-r--r--drivers/mmc/host/dw_mmc.c15
1 files changed, 11 insertions, 4 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 08c0592ed9bc..c4bddf6a5f1f 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -625,13 +625,13 @@ static void dw_mci_start_request(struct dw_mci *host,
625 host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); 625 host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
626} 626}
627 627
628/* must be called with host->lock held */
628static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, 629static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
629 struct mmc_request *mrq) 630 struct mmc_request *mrq)
630{ 631{
631 dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n", 632 dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
632 host->state); 633 host->state);
633 634
634 spin_lock_bh(&host->lock);
635 slot->mrq = mrq; 635 slot->mrq = mrq;
636 636
637 if (host->state == STATE_IDLE) { 637 if (host->state == STATE_IDLE) {
@@ -640,8 +640,6 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
640 } else { 640 } else {
641 list_add_tail(&slot->queue_node, &host->queue); 641 list_add_tail(&slot->queue_node, &host->queue);
642 } 642 }
643
644 spin_unlock_bh(&host->lock);
645} 643}
646 644
647static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) 645static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -651,14 +649,23 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
651 649
652 WARN_ON(slot->mrq); 650 WARN_ON(slot->mrq);
653 651
652 /*
653 * The check for card presence and queueing of the request must be
654 * atomic, otherwise the card could be removed in between and the
655 * request wouldn't fail until another card was inserted.
656 */
657 spin_lock_bh(&host->lock);
658
654 if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { 659 if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
660 spin_unlock_bh(&host->lock);
655 mrq->cmd->error = -ENOMEDIUM; 661 mrq->cmd->error = -ENOMEDIUM;
656 mmc_request_done(mmc, mrq); 662 mmc_request_done(mmc, mrq);
657 return; 663 return;
658 } 664 }
659 665
660 /* We don't support multiple blocks of weird lengths. */
661 dw_mci_queue_request(host, slot, mrq); 666 dw_mci_queue_request(host, slot, mrq);
667
668 spin_unlock_bh(&host->lock);
662} 669}
663 670
664static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 671static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)