mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
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>
This commit is contained in:
parent
b40af3aa77
commit
7456caae37
1 changed files with 11 additions and 4 deletions
|
@ -625,13 +625,13 @@ static void dw_mci_start_request(struct dw_mci *host,
|
||||||
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
|
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* must be called with host->lock held */
|
||||||
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
|
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
|
dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
|
||||||
host->state);
|
host->state);
|
||||||
|
|
||||||
spin_lock_bh(&host->lock);
|
|
||||||
slot->mrq = mrq;
|
slot->mrq = mrq;
|
||||||
|
|
||||||
if (host->state == STATE_IDLE) {
|
if (host->state == STATE_IDLE) {
|
||||||
|
@ -640,8 +640,6 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
|
||||||
} else {
|
} else {
|
||||||
list_add_tail(&slot->queue_node, &host->queue);
|
list_add_tail(&slot->queue_node, &host->queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_bh(&host->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
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)
|
||||||
|
|
||||||
WARN_ON(slot->mrq);
|
WARN_ON(slot->mrq);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The check for card presence and queueing of the request must be
|
||||||
|
* atomic, otherwise the card could be removed in between and the
|
||||||
|
* request wouldn't fail until another card was inserted.
|
||||||
|
*/
|
||||||
|
spin_lock_bh(&host->lock);
|
||||||
|
|
||||||
if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
|
if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
|
||||||
|
spin_unlock_bh(&host->lock);
|
||||||
mrq->cmd->error = -ENOMEDIUM;
|
mrq->cmd->error = -ENOMEDIUM;
|
||||||
mmc_request_done(mmc, mrq);
|
mmc_request_done(mmc, mrq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We don't support multiple blocks of weird lengths. */
|
|
||||||
dw_mci_queue_request(host, slot, mrq);
|
dw_mci_queue_request(host, slot, mrq);
|
||||||
|
|
||||||
|
spin_unlock_bh(&host->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
|
Loading…
Reference in a new issue