From: Pierre Ossman <drzeus@xxxxxxxxx> Erase blocks when upper layers discards them as that will increase performance in subsequent writes to those areas. Signed-off-by: Pierre Ossman <drzeus@xxxxxxxxx> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 3140e92..41d3000 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -376,6 +376,119 @@ static int mmc_blk_xfer_rq(struct mmc_blk_data *md, return ret; } +static int mmc_blk_erase_rq(struct mmc_blk_data *md, + struct request *req, unsigned int *bytes_xfered) +{ + struct mmc_card *card; + struct mmc_command cmd; + int ret; + + uint64_t start, end, blocks; + + BUG_ON(!bytes_xfered); + + *bytes_xfered = 0; + + card = md->queue.card; + + BUG_ON(mmc_card_blockaddr(card) && (card->csd.erase_size % 512)); + + start = req->sector << 9; + end = start + (req->nr_sectors << 9); + + /* + * The specs talk about the card removing the least + * significant bits, but the erase sizes are not guaranteed + * to be a power of two, so do a proper calculation. + */ + blocks = start; + if (do_div(blocks, card->csd.erase_size)) /* start % erase_size */ + start = (blocks + 1) * card->csd.erase_size; /* roundup() */ + blocks = end; + if (do_div(blocks, card->csd.erase_size)) + end = blocks * card->csd.erase_size; + + if (start == end) + goto out; + + /* + * The MMC spec isn't entirely clear that this should be done, + * but it would be impossible to erase the entire card if the + * addresses aren't sector based. + */ + if (mmc_card_blockaddr(card)) { + start >>= 9; + end >>= 9; + } + + if (mmc_card_sd(card)) + cmd.opcode = SD_ERASE_WR_BLK_START; + else + cmd.opcode = MMC_ERASE_GROUP_START; + cmd.arg = start; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + ret = mmc_wait_for_cmd(card->host, &cmd, 0); + if (ret) { + printk(KERN_ERR "%s: error %d setting block erase start address\n", + req->rq_disk->disk_name, ret); + return ret; + } + + if (mmc_card_sd(card)) + cmd.opcode = SD_ERASE_WR_BLK_END; + else + cmd.opcode = MMC_ERASE_GROUP_END; + cmd.arg = end - 1; /* the span is inclusive */ + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + ret = mmc_wait_for_cmd(card->host, &cmd, 0); + if (ret) { + printk(KERN_ERR "%s: error %d setting block erase end address\n", + req->rq_disk->disk_name, ret); + return ret; + } + + cmd.opcode = MMC_ERASE; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + ret = mmc_wait_for_cmd(card->host, &cmd, 0); + if (ret) { + printk(KERN_ERR "%s: error %d starting block erase\n", + req->rq_disk->disk_name, ret); + return ret; + } + + /* + * Wait for the card to finish the erase request... + */ + if (!mmc_host_is_spi(card->host)) { + do { + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + ret = mmc_wait_for_cmd(card->host, &cmd, 5); + if (ret) { + printk(KERN_ERR "%s: error %d requesting status\n", + req->rq_disk->disk_name, ret); + return ret; + } + /* + * Some cards mishandle the status bits, + * so make sure to check both the busy + * indication and the card state. + */ + } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(cmd.resp[0]) == 7)); + } + +out: + *bytes_xfered = req->nr_sectors << 9; + + return 0; +} + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; @@ -385,7 +498,10 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_claim_host(card->host); do { - err = mmc_blk_xfer_rq(md, req, &bytes_xfered); + if (blk_discard_rq(req)) + err = mmc_blk_erase_rq(md, req, &bytes_xfered); + else + err = mmc_blk_xfer_rq(md, req, &bytes_xfered); /* * First handle the sectors that got transferred diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 3dee97e..236a8e8 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -17,6 +17,7 @@ #include <linux/mmc/card.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> #include "queue.h" #define MMC_QUEUE_BOUNCESZ 65536 @@ -41,6 +42,12 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) return BLKPREP_OK; } +static int mmc_prep_discard(struct request_queue *q, struct request *req) +{ + /* This is just a dummy function to indicate erase support */ + return BLKPREP_OK; +} + static int mmc_queue_thread(void *d) { struct mmc_queue *mq = d; @@ -132,6 +139,17 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock blk_queue_prep_rq(mq->queue, mmc_prep_request); + if (card->csd.cmdclass & CCC_ERASE) + blk_queue_set_discard(mq->queue, mmc_prep_discard); + + /* + * Calculating a correct span is way to messy if this + * assumption is broken, so remove the erase support + */ + if (unlikely(mmc_card_blockaddr(card) && + (card->csd.erase_size % 512))) + blk_queue_set_discard(mq->queue, NULL); + #ifdef CONFIG_MMC_BLOCK_BOUNCE if (host->max_hw_segs == 1) { unsigned int bouncesz; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fdd7c76..5e09e36 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -113,7 +113,7 @@ static int mmc_decode_cid(struct mmc_card *card) static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; - unsigned int e, m, csd_struct; + unsigned int e, m, a, b, csd_struct; u32 *resp = card->raw_csd; /* @@ -150,6 +150,11 @@ static int mmc_decode_csd(struct mmc_card *card) csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + a = UNSTUFF_BITS(resp, 42, 5); + b = UNSTUFF_BITS(resp, 37, 5); + csd->erase_size = (a + 1) * (b + 1); + csd->erase_size <<= csd->write_blkbits; + return 0; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 26fc098..3024798 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -118,6 +118,13 @@ static int mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + + if (UNSTUFF_BITS(resp, 46, 1)) + csd->erase_size = 512; + else { + csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; + csd->erase_size <<= csd->write_blkbits; + } break; case 1: /* @@ -146,6 +153,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = 4; /* Unused */ csd->write_blkbits = 9; csd->write_partial = 0; + csd->erase_size = 512; break; default: printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index ee6e822..8abdf72 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -30,6 +30,7 @@ struct mmc_csd { unsigned int tacc_ns; unsigned int r2w_factor; unsigned int max_dtr; + unsigned int erase_size; unsigned int read_blkbits; unsigned int write_blkbits; unsigned int capacity; diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h index f310062..1f6f316 100644 --- a/include/linux/mmc/sd.h +++ b/include/linux/mmc/sd.h @@ -21,6 +21,10 @@ /* class 10 */ #define SD_SWITCH 6 /* adtc [31:0] See below R1 */ + /* class 5 */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ + /* 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 */ -- -- Pierre Ossman Linux kernel, MMC maintainer http://www.kernel.org rdesktop, core developer http://www.rdesktop.org WARNING: This correspondence is being monitored by the Swedish government. Make sure your server uses encryption for SMTP traffic and consider using PGP for end-to-end encryption. -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html