diff options
author | James Hogan <james.hogan@imgtec.com> | 2011-06-24 08:55:10 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-07-20 17:20:58 -0400 |
commit | 7456caae37396fc1bc6f8e9461d07664b8c2f280 (patch) | |
tree | 972f231756e8e766aa367689085a2834c7f6fcb4 /drivers/mmc/host/dw_mmc.c | |
parent | b40af3aa7712e8f1b73e00e781cd93181483f649 (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.c | 15 |
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 */ | ||
628 | static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, | 629 | static 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 | ||
647 | static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) | 645 | static 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 | ||
664 | static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 671 | static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |