linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Christian Löhle" <CLoehle@hyperstone.com>
To: "linux-mmc@vger.kernel.org" <linux-mmc@vger.kernel.org>,
	"ulf.hansson@linaro.org" <ulf.hansson@linaro.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	Avri Altman <Avri.Altman@wdc.com>
Cc: Christoph Hellwig <hch@infradead.org>
Subject: [PATCH] mmc: block: Differentiate busy and non-TRAN state
Date: Tue, 6 Jul 2021 08:20:07 +0000	[thread overview]
Message-ID: <CWXP265MB26807AC3C130772D789D0AABC41B9@CWXP265MB2680.GBRP265.PROD.OUTLOOK.COM> (raw)
In-Reply-To: <CWXP265MB268049D9AB181062DA7F6DDBC4009@CWXP265MB2680.GBRP265.PROD.OUTLOOK.COM>

Prevent race condition with ioctl commands

Wait for both, a card no longer signalling busy
and it being returned back to TRAN state.
A card not signaling busy does not mean it is
ready to accept new regular commands.
Instead for a command to be done it should not only
no longer signal busy but also return back to TRAN state,
at least for commands that eventually transition back
to TRAN. Otherwise the next ioctl command may be rejected
as the card is still in PROG state after the previous command.

Signed-off-by: Christian Loehle <cloehle@hyperstone.com>
---
 drivers/mmc/core/block.c | 84 +++++++++++++++++++++++++++++++++++-----
 include/linux/mmc/mmc.h  |  9 +++--
 2 files changed, 80 insertions(+), 13 deletions(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 88f4c215caa6..dda10ccee37f 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -411,7 +411,32 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
 	return 0;
 }
 
-static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
+static int is_return_to_tran_cmd(struct mmc_command *cmd)
+{
+	/*
+	 * Cards will never return to TRAN after completing
+	 * identification commands or MMC_SEND_STATUS if they are not selected.
+	 */
+	switch (cmd->opcode) {
+	case MMC_GO_IDLE_STATE:
+	case MMC_SEND_OP_COND:
+	case MMC_ALL_SEND_CID:
+	case MMC_SET_RELATIVE_ADDR:
+	case MMC_SET_DSR:
+	case MMC_SLEEP_AWAKE:
+	case MMC_SELECT_CARD:
+	case MMC_SEND_CSD:
+	case MMC_SEND_CID:
+	case MMC_SEND_STATUS:
+	case MMC_GO_INACTIVE_STATE:
+	case MMC_APP_CMD:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static int card_poll_until_tran(struct mmc_card *card, unsigned int timeout_ms,
 			    u32 *resp_errs)
 {
 	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -433,8 +458,7 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
 			*resp_errs |= status;
 
 		/*
-		 * Timeout if the device never becomes ready for data and never
-		 * leaves the program state.
+		 * Timeout if the device never returns to TRAN state.
 		 */
 		if (done) {
 			dev_err(mmc_dev(card->host),
@@ -442,6 +466,41 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
 				 __func__, status);
 			return -ETIMEDOUT;
 		}
+	} while (R1_CURRENT_STATE(status) != R1_STATE_TRAN);
+
+	return err;
+}
+
+static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
+			    u32 *resp_errs)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+	int err = 0;
+	u32 status;
+
+	do {
+		bool done = time_after(jiffies, timeout);
+
+		err = __mmc_send_status(card, &status, 5);
+		if (err) {
+			dev_err(mmc_dev(card->host),
+				"error %d requesting status\n", err);
+			return err;
+		}
+
+		/* Accumulate any response error bits seen */
+		if (resp_errs)
+			*resp_errs |= status;
+
+		/*
+		 * Timeout if the device never becomes ready for data.
+		 */
+		if (done) {
+			dev_err(mmc_dev(card->host),
+				"Card remained busy! %s status: %#x\n",
+				 __func__, status);
+			return -ETIMEDOUT;
+		}
 	} while (!mmc_ready_for_data(status));
 
 	return err;
@@ -596,12 +655,19 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
 
 	if (idata->rpmb || (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
 		/*
-		 * Ensure RPMB/R1B command has completed by polling CMD13
-		 * "Send Status".
+		 * Ensure card is no longer signalling busy by polling CMD13.
 		 */
 		err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, NULL);
 	}
 
+	if (is_return_to_tran_cmd(&cmd)) {
+		/*
+		 * Ensure card has returned back to TRAN state (e.g. from PROG)
+		 * and is ready to accept a new command.
+		 */
+		err = card_poll_until_tran(card, MMC_BLK_TIMEOUT_MS, NULL);
+	}
+
 	return err;
 }
 
@@ -1630,7 +1696,7 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req)
 
 	mmc_blk_send_stop(card, timeout);
 
-	err = card_busy_detect(card, timeout, NULL);
+	err = card_poll_until_tran(card, timeout, NULL);
 
 	mmc_retune_release(card->host);
 
@@ -1662,7 +1728,7 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
 			goto error_exit;
 
 		if (!mmc_host_is_spi(host) &&
-		    !mmc_ready_for_data(status)) {
+		    !mmc_tran_and_ready_for_data(status)) {
 			err = mmc_blk_fix_state(card, req);
 			if (err)
 				goto error_exit;
@@ -1784,7 +1850,7 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req)
 
 	/* Try to get back to "tran" state */
 	if (!mmc_host_is_spi(mq->card->host) &&
-	    (err || !mmc_ready_for_data(status)))
+	    (err || !mmc_tran_and_ready_for_data(status)))
 		err = mmc_blk_fix_state(mq->card, req);
 
 	/*
@@ -1854,7 +1920,7 @@ static int mmc_blk_card_busy(struct mmc_card *card, struct request *req)
 	if (mmc_host_is_spi(card->host) || rq_data_dir(req) == READ)
 		return 0;
 
-	err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, &status);
+	err = card_poll_until_tran(card, MMC_BLK_TIMEOUT_MS, &status);
 
 	/*
 	 * Do not assume data transferred correctly if there are any error bits
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index d9a65c6a8816..9ae27504cbc9 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -163,10 +163,11 @@ static inline bool mmc_op_multi(u32 opcode)
 
 static inline bool mmc_ready_for_data(u32 status)
 {
-	/*
-	 * Some cards mishandle the status bits, so make sure to check both the
-	 * busy indication and the card state.
-	 */
+	return status & R1_READY_FOR_DATA;
+}
+
+static inline bool mmc_tran_and_ready_for_data(u32 status)
+{
 	return status & R1_READY_FOR_DATA &&
 	       R1_CURRENT_STATE(status) == R1_STATE_TRAN;
 }
-- 
2.32.0

Hyperstone GmbH | Line-Eid-Strasse 3 | 78467 Konstanz
Managing Directors: Dr. Jan Peter Berns.
Commercial register of local courts: Freiburg HRB381782


  parent reply	other threads:[~2021-07-06  8:20 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-01  9:43 [PATCH] mmc: block: Differentiate busy and non-TRAN state Christian Löhle
2021-07-02  7:43 ` Christoph Hellwig
2021-07-06  8:20 ` Christian Löhle [this message]
2021-07-06  8:40   ` Ulf Hansson
2021-07-06  9:09     ` Christian Löhle
2021-07-06  9:34       ` Ulf Hansson
2021-07-07  6:49       ` Avri Altman
2021-07-07  8:27         ` [PATCHv2] mmc: block: Differentiate busy and PROG state Christian Löhle
2021-07-07 11:57           ` Ulf Hansson
2021-07-07 14:36             ` Christian Löhle
2021-08-09 15:16               ` Ulf Hansson
2021-07-07  9:01         ` [PATCH] mmc: block: Differentiate busy and non-TRAN state Christian Löhle
2021-07-07 10:16           ` Avri Altman
2021-07-07 14:42             ` Christian Löhle

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CWXP265MB26807AC3C130772D789D0AABC41B9@CWXP265MB2680.GBRP265.PROD.OUTLOOK.COM \
    --to=cloehle@hyperstone.com \
    --cc=Avri.Altman@wdc.com \
    --cc=hch@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=ulf.hansson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).