[PATCH 2/2] mmc_block: erase discarded blocks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux