Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7456caae authored by James Hogan's avatar James Hogan Committed by Chris Ball
Browse files

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: default avatarJames Hogan <james.hogan@imgtec.com>
Acked-by: default avatarWill Newton <will.newton@imgtec.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent b40af3aa
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -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) {
@@ -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)
@@ -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)