Skip to content

Commit

Permalink
mmc: dw_mmc: fix race with request and removal
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
James Hogan authored and Chris Ball committed Jul 20, 2011
1 parent b40af3a commit 7456caa
Showing 1 changed file with 11 additions and 4 deletions.
15 changes: 11 additions & 4 deletions drivers/mmc/host/dw_mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/* must be called with host->lock held */
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
struct mmc_request *mrq)
{
dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
host->state);

spin_lock_bh(&host->lock);
slot->mrq = mrq;

if (host->state == STATE_IDLE) {
Expand All @@ -640,8 +640,6 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
} else {
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)
Expand All @@ -651,14 +649,23 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *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)) {
spin_unlock_bh(&host->lock);
mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
return;
}

/* We don't support multiple blocks of weird lengths. */
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)
Expand Down

0 comments on commit 7456caa

Please sign in to comment.