2006-10-01 12:43:24

by Pierre Ossman

[permalink] [raw]
Subject: [PATCH] [MMC] Multi sector write transfers

SD cards extend the protocol by allowing the host to query a card how many
blocks were successfully stored on the medium. This allows us to safely write
chunks of blocks at once.

Signed-off-by: Pierre Ossman <[email protected]>
---

drivers/mmc/mmc_block.c | 104 ++++++++++++++++++++++++++++++++++++++----
include/linux/mmc/protocol.h | 1
2 files changed, 96 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index c1293f1..f9027c8 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -28,6 +28,7 @@ #include <linux/hdreg.h>
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/scatterlist.h>

#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -154,6 +155,71 @@ static int mmc_blk_prep_rq(struct mmc_qu
return stat;
}

+static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
+{
+ int err;
+ u32 blocks;
+
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ unsigned int timeout_us;
+
+ struct scatterlist sg;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_APP_CMD;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD))
+ return (u32)-1;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ data.timeout_ns = card->csd.tacc_ns * 100;
+ data.timeout_clks = card->csd.tacc_clks * 100;
+
+ timeout_us = data.timeout_ns / 1000;
+ timeout_us += data.timeout_clks * 1000 /
+ (card->host->ios.clock / 1000);
+
+ if (timeout_us > 100000) {
+ data.timeout_ns = 100000000;
+ data.timeout_clks = 0;
+ }
+
+ data.blksz = 4;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ sg_init_one(&sg, &blocks, 4);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE)
+ return (u32)-1;
+
+ blocks = ntohl(blocks);
+
+ return blocks;
+}
+
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
@@ -184,10 +250,13 @@ static int mmc_blk_issue_rq(struct mmc_q

/*
* If the host doesn't support multiple block writes, force
- * block writes to single block.
+ * block writes to single block. SD cards are excepted from
+ * this rule as they support querying the number of
+ * successfully written sectors.
*/
if (rq_data_dir(req) != READ &&
- !(card->host->caps & MMC_CAP_MULTIWRITE))
+ !(card->host->caps & MMC_CAP_MULTIWRITE) &&
+ !mmc_card_sd(card))
brq.data.blocks = 1;

if (brq.data.blocks > 1) {
@@ -276,24 +345,41 @@ #endif
return 1;

cmd_err:
- mmc_card_release_host(card);
-
ret = 1;

- /*
- * For writes and where the host claims to support proper
- * error reporting, we first ok the successful blocks.
+ /*
+ * If this is an SD card and we're writing, we can first
+ * mark the known good sectors as ok.
+ *
+ * If the card is not SD, we can still ok written sectors
+ * if the controller can do proper error reporting.
*
* For reads we just fail the entire chunk as that should
* be safe in all cases.
*/
- if (rq_data_dir(req) != READ &&
- (card->host->caps & MMC_CAP_MULTIWRITE)) {
+ if (rq_data_dir(req) != READ && mmc_card_sd(card)) {
+ u32 blocks;
+ unsigned int bytes;
+
+ blocks = mmc_sd_num_wr_blocks(card);
+ if (blocks != (u32)-1) {
+ if (card->csd.write_partial)
+ bytes = blocks << md->block_bits;
+ else
+ bytes = blocks << 9;
+ spin_lock_irq(&md->lock);
+ ret = end_that_request_chunk(req, 1, bytes);
+ spin_unlock_irq(&md->lock);
+ }
+ } else if (rq_data_dir(req) != READ &&
+ (card->host->caps & MMC_CAP_MULTIWRITE)) {
spin_lock_irq(&md->lock);
ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered);
spin_unlock_irq(&md->lock);
}

+ mmc_card_release_host(card);
+
spin_lock_irq(&md->lock);
while (ret) {
ret = end_that_request_chunk(req, 0,
diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h
index 81c3f77..08dec8d 100644
--- a/include/linux/mmc/protocol.h
+++ b/include/linux/mmc/protocol.h
@@ -83,6 +83,7 @@ #define SD_SEND_RELATIVE_ADDR 3 /*

/* Application commands */
#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
+#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */



2006-10-03 22:20:15

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] [MMC] Multi sector write transfers


Russell is moving away from mmc maintainership so we'll need to do
something different here.

The patches look good to my untrained eye, except...

On Sun, 01 Oct 2006 14:42:57 +0200
Pierre Ossman <[email protected]> wrote:

> SD cards extend the protocol by allowing the host to query a card how many
> blocks were successfully stored on the medium. This allows us to safely write
> chunks of blocks at once.

I recall Russell nacked multisector mmc-writing when it came up six or
twelve months ago. I don't recall the exact details - lack of trust in
manufacturers supporting it correctly?

Is that concern relevant to this patch?

2006-10-05 06:42:19

by Pierre Ossman

[permalink] [raw]
Subject: Re: [PATCH] [MMC] Multi sector write transfers

Andrew Morton wrote:
> Russell is moving away from mmc maintainership so we'll need to do
> something different here.
>
> The patches look good to my untrained eye, except...
>
> On Sun, 01 Oct 2006 14:42:57 +0200
> Pierre Ossman <[email protected]> wrote:
>
>> SD cards extend the protocol by allowing the host to query a card how many
>> blocks were successfully stored on the medium. This allows us to safely write
>> chunks of blocks at once.
>
> I recall Russell nacked multisector mmc-writing when it came up six or
> twelve months ago. I don't recall the exact details - lack of trust in
> manufacturers supporting it correctly?
>
> Is that concern relevant to this patch?

The concern then was that we enabled multi-sector writes across the
board. Russell's concern was that some host controllers weren't properly
reporting back how far they had gotten when a failure occurred. We now
have two solutions to this:

* Let the drivers set a flag, indicating that the controller properly
reports the number of sent blocks, even when a fault occurs. Russell put
together a patch for this and it is already in Linus' tree.

* Ask the card, not the controller, how many blocks got there ok. This
is only supported by SD though, so MMC still use only one block per
write (except for the case above).

The only problem I see is if the card has decided to completely shut
down so it's impossible to query the number of blocks written. In this
case, reporting correctly to the upper layers might not be a big issue
(as they can't take any action on the card anyway), so I do not see it
as a problem in practice. I have yet to see any comments on the patch
though (other than end users that like the speed boost).

Rgds
Pierre