2010-06-24 08:44:21

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V3 0/5] Add MMC erase and secure erase V3

Hi

Add ability to do MMC erase and secure erase operations from userspace,
using the BLKDISCARD ioctl and a newly created BLKSECDISCARD ioctl.

This is version 3 of these patches.

Changes from V2

- move the addition of BLKSECDISCARD to a separate patch and implement it
using I/O requests
- move the MMC support of secure discard to a separate patch and support
the secure discard I/O request

Changes from V1

- use discard I/O requests instead of implementing BLKDISCARD in mmc_block
- do not validate erase size for MMC erases, instead round to erase size
- rename MMC max_erase to pref_erase and expose it through sysfs as
preferred_erase_size


Adrian Hunter (5):
mmc: Add erase, secure erase, trim and secure trim operations
mmc_block: Add discard support
omap_hsmmc: Add erase capability
block: Add secure discard
mmc_block: Add support for secure discard

block/blk-core.c | 6 +-
block/blk-lib.c | 6 +
block/compat_ioctl.c | 1 +
block/elevator.c | 4 +
block/ioctl.c | 15 ++-
drivers/mmc/card/block.c | 82 ++++++++++-
drivers/mmc/card/queue.c | 19 ++-
drivers/mmc/core/core.c | 346 +++++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/mmc.c | 47 ++++++-
drivers/mmc/core/sd.c | 82 ++++++++++
drivers/mmc/core/sd_ops.c | 48 ++++++
drivers/mmc/core/sd_ops.h | 1 +
drivers/mmc/host/omap_hsmmc.c | 13 ++-
include/linux/bio.h | 7 +-
include/linux/blkdev.h | 10 +-
include/linux/fs.h | 2 +
include/linux/mmc/card.h | 19 +++
include/linux/mmc/core.h | 19 +++
include/linux/mmc/host.h | 1 +
include/linux/mmc/mmc.h | 24 ++-
include/linux/mmc/sd.h | 5 +
kernel/trace/blktrace.c | 8 +
23 files changed, 746 insertions(+), 21 deletions(-)


Regards
Adrian


2010-06-24 08:44:51

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V3 1/5] mmc: Add erase, secure erase, trim and secure trim operations

>From c7a9eb435367fd8ef16e79c053f0c5a630473830 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Tue, 1 Jun 2010 13:20:22 +0300
Subject: [PATCH 1/5] mmc: Add erase, secure erase, trim and secure trim operations

SD/MMC cards tend to support an erase operation. In addition,
eMMC v4.4 cards can support secure erase, trim and secure trim
operations that are all variants of the basic erase command.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 346 +++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/mmc.c | 47 ++++++-
drivers/mmc/core/sd.c | 82 +++++++++++
drivers/mmc/core/sd_ops.c | 48 ++++++
drivers/mmc/core/sd_ops.h | 1 +
include/linux/mmc/card.h | 19 +++
include/linux/mmc/core.h | 19 +++
include/linux/mmc/host.h | 1 +
include/linux/mmc/mmc.h | 24 +++-
include/linux/mmc/sd.h | 5 +
11 files changed, 587 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 569e94d..3b8b1b7 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1050,6 +1050,352 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)

EXPORT_SYMBOL(mmc_detect_change);

+void mmc_init_erase(struct mmc_card *card)
+{
+ unsigned int sz;
+
+ if (is_power_of_2(card->erase_size))
+ card->erase_shift = ffs(card->erase_size) - 1;
+ else
+ card->erase_shift = 0;
+
+ /*
+ * It is possible to erase an arbitrarily large area of an SD or MMC
+ * card. That is not desirable because it can take a long time
+ * (minutes) potentially delaying more important I/O, and also the
+ * timeout calculations become increasingly hugely over-estimated.
+ * Consequently, 'pref_erase' is defined as a guide to limit erases
+ * to that size and alignment.
+ *
+ * For SD cards that define Allocation Unit size, limit erases to one
+ * Allocation Unit at a time. For MMC cards that define High Capacity
+ * Erase Size, whether it is switched on or not, limit to that size.
+ * Otherwise just have a stab at a good value. For modern cards it
+ * will end up being 4MiB. Note that if the value is too small, it
+ * can end up taking longer to erase.
+ */
+ if (mmc_card_sd(card) && card->ssr.au) {
+ card->pref_erase = card->ssr.au;
+ card->erase_shift = ffs(card->ssr.au) - 1;
+ } else if (card->ext_csd.hc_erase_size) {
+ card->pref_erase = card->ext_csd.hc_erase_size;
+ } else {
+ sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
+ if (sz < 128)
+ card->pref_erase = 512 * 1024 / 512;
+ else if (sz < 512)
+ card->pref_erase = 1024 * 1024 / 512;
+ else if (sz < 1024)
+ card->pref_erase = 2 * 1024 * 1024 / 512;
+ else
+ card->pref_erase = 4 * 1024 * 1024 / 512;
+ if (card->pref_erase < card->erase_size)
+ card->pref_erase = card->erase_size;
+ else {
+ sz = card->pref_erase % card->erase_size;
+ if (sz)
+ card->pref_erase += card->erase_size - sz;
+ }
+ }
+}
+
+static void mmc_set_mmc_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd,
+ unsigned int arg, unsigned int qty)
+{
+ unsigned int erase_timeout;
+
+ if (card->ext_csd.erase_group_def & 1) {
+ /* High Capacity Erase Group Size uses HC timeouts */
+ if (arg == MMC_TRIM_ARG)
+ erase_timeout = card->ext_csd.trim_timeout;
+ else
+ erase_timeout = card->ext_csd.hc_erase_timeout;
+ } else {
+ /* CSD Erase Group Size uses write timeout */
+ unsigned int mult = (10 << card->csd.r2w_factor);
+ unsigned int timeout_clks = card->csd.tacc_clks * mult;
+ unsigned int timeout_us;
+
+ /* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */
+ if (card->csd.tacc_ns < 1000000)
+ timeout_us = (card->csd.tacc_ns * mult) / 1000;
+ else
+ timeout_us = (card->csd.tacc_ns / 1000) * mult;
+
+ /*
+ * ios.clock is only a target. The real clock rate might be
+ * less but not that much less, so fudge it by multiplying by 2.
+ */
+ timeout_clks <<= 1;
+ timeout_us += (timeout_clks * 1000) /
+ (card->host->ios.clock / 1000);
+
+ erase_timeout = timeout_us / 1000;
+
+ /*
+ * Theoretically, the calculation could underflow so round up
+ * to 1ms in that case.
+ */
+ if (!erase_timeout)
+ erase_timeout = 1;
+ }
+
+ /* Multiplier for secure operations */
+ if (arg & MMC_SECURE_ARGS) {
+ if (arg == MMC_SECURE_ERASE_ARG)
+ erase_timeout *= card->ext_csd.sec_erase_mult;
+ else
+ erase_timeout *= card->ext_csd.sec_trim_mult;
+ }
+
+ erase_timeout *= qty;
+
+ /*
+ * Ensure at least a 1 second timeout for SPI as per
+ * 'mmc_set_data_timeout()'
+ */
+ if (mmc_host_is_spi(card->host) && erase_timeout < 1000)
+ erase_timeout = 1000;
+
+ cmd->erase_timeout = erase_timeout;
+}
+
+static void mmc_set_sd_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd, unsigned int arg,
+ unsigned int qty)
+{
+ if (card->ssr.erase_timeout) {
+ /* Erase timeout specified in SD Status Register (SSR) */
+ cmd->erase_timeout = card->ssr.erase_timeout * qty +
+ card->ssr.erase_offset;
+ } else {
+ /*
+ * Erase timeout not specified in SD Status Register (SSR) so
+ * use 250ms per write block.
+ */
+ cmd->erase_timeout = 250 * qty;
+ }
+
+ /* Must not be less than 1 second */
+ if (cmd->erase_timeout < 1000)
+ cmd->erase_timeout = 1000;
+}
+
+static void mmc_set_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd, unsigned int arg,
+ unsigned int qty)
+{
+ if (mmc_card_sd(card))
+ mmc_set_sd_erase_timeout(card, cmd, arg, qty);
+ else
+ mmc_set_mmc_erase_timeout(card, cmd, arg, qty);
+}
+
+static int mmc_do_erase(struct mmc_card *card, unsigned int from,
+ unsigned int to, unsigned int arg)
+{
+ struct mmc_command cmd;
+ unsigned int qty = 0;
+ int err;
+
+ /*
+ * qty is used to calculate the erase timeout which depends on how many
+ * erase groups (or allocation units in SD terminology) are affected.
+ * We count erasing part of an erase group as one erase group.
+ * For SD, the allocation units are always a power of 2. For MMC, the
+ * erase group size is almost certainly also power of 2, but it does not
+ * seem to insist on that in the JEDEC standard, so we fall back to
+ * division in that case. SD may not specify an allocation unit size,
+ * in which case the timeout is based on the number of write blocks.
+ *
+ * Note that the timeout for secure trim 2 will only be correct if the
+ * number of erase groups specified is the same as the total of all
+ * preceding secure trim 1 commands. Since the power may have been
+ * lost since the secure trim 1 commands occurred, it is generally
+ * impossible to calculate the secure trim 2 timeout correctly.
+ */
+ if (card->erase_shift)
+ qty += ((to >> card->erase_shift) -
+ (from >> card->erase_shift)) + 1;
+ else if (mmc_card_sd(card))
+ qty += to - from + 1;
+ else
+ qty += ((to / card->erase_size) -
+ (from / card->erase_size)) + 1;
+
+ if (!mmc_card_blockaddr(card)) {
+ from <<= 9;
+ to <<= 9;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ if (mmc_card_sd(card))
+ cmd.opcode = SD_ERASE_WR_BLK_START;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_START;
+ cmd.arg = from;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: group start error %d, "
+ "status %#x\n", err, cmd.resp[0]);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ if (mmc_card_sd(card))
+ cmd.opcode = SD_ERASE_WR_BLK_END;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_END;
+ cmd.arg = to;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n",
+ err, cmd.resp[0]);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_ERASE;
+ cmd.arg = arg;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ mmc_set_erase_timeout(card, &cmd, arg, qty);
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
+ err, cmd.resp[0]);
+ err = -EIO;
+ goto out;
+ }
+
+ if (mmc_host_is_spi(card->host))
+ goto out;
+
+ do {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ /* Do not retry else we can't see errors */
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err || (cmd.resp[0] & 0xFDF92000)) {
+ printk(KERN_ERR "error %d requesting status %#x\n",
+ err, cmd.resp[0]);
+ err = -EIO;
+ goto out;
+ }
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
+ R1_CURRENT_STATE(cmd.resp[0]) == 7);
+out:
+ return err;
+}
+
+/**
+ * mmc_erase - erase sectors.
+ * @card: card to erase
+ * @from: first sector to erase
+ * @nr: number of sectors to erase
+ * @arg: erase command argument (SD supports only %MMC_ERASE_ARG)
+ *
+ * Caller must claim host before calling this function.
+ */
+int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg)
+{
+ unsigned int rem, to = from + nr;
+
+ if (!(card->host->caps & MMC_CAP_ERASE) ||
+ !(card->csd.cmdclass & CCC_ERASE))
+ return -EOPNOTSUPP;
+
+ if (!card->erase_size)
+ return -EOPNOTSUPP;
+
+ if (mmc_card_sd(card) && arg != MMC_ERASE_ARG)
+ return -EOPNOTSUPP;
+
+ if ((arg & MMC_SECURE_ARGS) &&
+ !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN))
+ return -EOPNOTSUPP;
+
+ if ((arg & MMC_TRIM_ARGS) &&
+ !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN))
+ return -EOPNOTSUPP;
+
+ if (arg == MMC_SECURE_ERASE_ARG) {
+ if (from % card->erase_size || nr % card->erase_size)
+ return -EINVAL;
+ }
+
+ if (arg == MMC_ERASE_ARG) {
+ rem = from % card->erase_size;
+ if (rem) {
+ rem = card->erase_size - rem;
+ from += rem;
+ if (nr > rem)
+ nr -= rem;
+ else
+ return 0;
+ }
+ rem = nr % card->erase_size;
+ if (rem)
+ nr -= rem;
+ }
+
+ if (nr == 0)
+ return 0;
+
+ to = from + nr;
+
+ if (to <= from)
+ return -EINVAL;
+
+ /* 'from' and 'to' are inclusive */
+ to -= 1;
+
+ return mmc_do_erase(card, from, to, arg);
+}
+EXPORT_SYMBOL(mmc_erase);
+
+int mmc_can_erase(struct mmc_card *card)
+{
+ if ((card->host->caps & MMC_CAP_ERASE) &&
+ (card->csd.cmdclass & CCC_ERASE) && card->erase_size)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_erase);
+
+int mmc_can_trim(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_trim);
+
+int mmc_can_secure_erase_trim(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_secure_erase_trim);
+
+int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
+ unsigned int nr)
+{
+ if (!card->erase_size)
+ return 0;
+ if (from % card->erase_size || nr % card->erase_size)
+ return 0;
+ return 1;
+}
+EXPORT_SYMBOL(mmc_erase_group_aligned);

void mmc_rescan(struct work_struct *work)
{
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a811c52..9d9eef5 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -29,6 +29,8 @@ struct mmc_bus_ops {
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);

+void mmc_init_erase(struct mmc_card *card);
+
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 89f7a25..1cf5344 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -108,13 +108,23 @@ static int mmc_decode_cid(struct mmc_card *card)
return 0;
}

+static void mmc_set_erase_size(struct mmc_card *card)
+{
+ if (card->ext_csd.erase_group_def & 1)
+ card->erase_size = card->ext_csd.hc_erase_size;
+ else
+ card->erase_size = card->csd.erase_size;
+
+ mmc_init_erase(card);
+}
+
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
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;

/*
@@ -151,6 +161,13 @@ 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);

+ if (csd->write_blkbits >= 9) {
+ 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 - 9;
+ }
+
return 0;
}

@@ -247,8 +264,30 @@ static int mmc_read_ext_csd(struct mmc_card *card)
if (sa_shift > 0 && sa_shift <= 0x17)
card->ext_csd.sa_timeout =
1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
+ card->ext_csd.erase_group_def =
+ ext_csd[EXT_CSD_ERASE_GROUP_DEF];
+ card->ext_csd.hc_erase_timeout = 300 *
+ ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
+ card->ext_csd.hc_erase_size =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10;
+ }
+
+ if (card->ext_csd.rev >= 4) {
+ card->ext_csd.sec_trim_mult =
+ ext_csd[EXT_CSD_SEC_TRIM_MULT];
+ card->ext_csd.sec_erase_mult =
+ ext_csd[EXT_CSD_SEC_ERASE_MULT];
+ card->ext_csd.sec_feature_support =
+ ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
+ card->ext_csd.trim_timeout = 300 *
+ ext_csd[EXT_CSD_TRIM_MULT];
}

+ if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+
out:
kfree(ext_csd);

@@ -260,6 +299,8 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
+MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
@@ -271,6 +312,8 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_date.attr,
+ &dev_attr_erase_size.attr,
+ &dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
@@ -407,6 +450,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_read_ext_csd(card);
if (err)
goto free_card;
+ /* Erase size depends on CSD and Extended CSD */
+ mmc_set_erase_size(card);
}

/*
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5eac21d..801e0d8 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -119,6 +119,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 = 1;
+ } else if (csd->write_blkbits >= 9) {
+ csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1;
+ csd->erase_size <<= csd->write_blkbits - 9;
+ }
break;
case 1:
/*
@@ -147,6 +154,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 = 1;
break;
default:
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
@@ -154,6 +162,8 @@ static int mmc_decode_csd(struct mmc_card *card)
return -EINVAL;
}

+ card->erase_size = csd->erase_size;
+
return 0;
}

@@ -179,10 +189,68 @@ static int mmc_decode_scr(struct mmc_card *card)
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);

+ if (UNSTUFF_BITS(resp, 55, 1))
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+
return 0;
}

/*
+ * Fetch and process SD Status register.
+ */
+static int mmc_read_ssr(struct mmc_card *card)
+{
+ unsigned int au, es, et, eo;
+ int err, i;
+ u32 *ssr;
+
+ if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
+ printk(KERN_WARNING "%s: card lacks mandatory SD Status "
+ "function.\n", mmc_hostname(card->host));
+ return 0;
+ }
+
+ ssr = kmalloc(64, GFP_KERNEL);
+ if (!ssr)
+ return -ENOMEM;
+
+ err = mmc_app_sd_status(card, ssr);
+ if (err) {
+ printk(KERN_WARNING "%s: problem reading SD Status "
+ "register.\n", mmc_hostname(card->host));
+ err = 0;
+ goto out;
+ }
+
+ for (i = 0; i < 16; i++)
+ ssr[i] = be32_to_cpu(ssr[i]);
+
+ /*
+ * UNSTUFF_BITS only works with four u32s so we have to offset the
+ * bitfield positions accordingly.
+ */
+ au = UNSTUFF_BITS(ssr, 428 - 384, 4);
+ if (au > 0 || au <= 9) {
+ card->ssr.au = 1 << (au + 4);
+ es = UNSTUFF_BITS(ssr, 408 - 384, 16);
+ et = UNSTUFF_BITS(ssr, 402 - 384, 6);
+ eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
+ if (es && et) {
+ card->ssr.erase_timeout = (et * 1000) / es;
+ card->ssr.erase_offset = eo * 1000;
+ }
+ } else {
+ printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit "
+ "size.\n", mmc_hostname(card->host));
+ }
+out:
+ kfree(ssr);
+ return err;
+}
+
+/*
* Fetches and decodes switch information
*/
static int mmc_read_switch(struct mmc_card *card)
@@ -289,6 +357,8 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
+MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
@@ -302,6 +372,8 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_csd.attr,
&dev_attr_scr.attr,
&dev_attr_date.attr,
+ &dev_attr_erase_size.attr,
+ &dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
@@ -442,6 +514,16 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
goto free_card;

/*
+ * Fetch and process SD Status register.
+ */
+ err = mmc_read_ssr(card);
+ if (err)
+ goto free_card;
+
+ /* Erase init depends on CSD and SSR */
+ mmc_init_erase(card);
+
+ /*
* Fetch switch information from card.
*/
err = mmc_read_switch(card);
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 63772e7..797cdb5 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -346,3 +346,51 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
return 0;
}

+int mmc_app_sd_status(struct mmc_card *card, void *ssr)
+{
+ int err;
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+ BUG_ON(!ssr);
+
+ /* NOTE: caller guarantees ssr is heap-allocated */
+
+ err = mmc_app_cmd(card->host, card);
+ if (err)
+ return err;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = SD_APP_SD_STATUS;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, ssr, 64);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index 9742d8a..ffc2305 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -19,6 +19,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
+int mmc_app_sd_status(struct mmc_card *card, void *ssr);

#endif

diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d02d2c6..750c963 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; /* In sectors */
unsigned int read_blkbits;
unsigned int write_blkbits;
unsigned int capacity;
@@ -41,9 +42,16 @@ struct mmc_csd {

struct mmc_ext_csd {
u8 rev;
+ u8 erase_group_def;
+ u8 sec_feature_support;
unsigned int sa_timeout; /* Units: 100ns */
unsigned int hs_max_dtr;
unsigned int sectors;
+ unsigned int hc_erase_size; /* In sectors */
+ unsigned int hc_erase_timeout; /* In milliseconds */
+ unsigned int sec_trim_mult; /* Secure trim multiplier */
+ unsigned int sec_erase_mult; /* Secure erase multiplier */
+ unsigned int trim_timeout; /* In milliseconds */
};

struct sd_scr {
@@ -53,6 +61,12 @@ struct sd_scr {
#define SD_SCR_BUS_WIDTH_4 (1<<2)
};

+struct sd_ssr {
+ unsigned int au; /* In sectors */
+ unsigned int erase_timeout; /* In milliseconds */
+ unsigned int erase_offset; /* In milliseconds */
+};
+
struct sd_switch_caps {
unsigned int hs_max_dtr;
};
@@ -101,6 +115,10 @@ struct mmc_card {
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
/* for byte mode */
+ unsigned int erase_size; /* erase size in sectors */
+ unsigned int erase_shift; /* if erase unit is power 2 */
+ unsigned int pref_erase; /* in sectors */
+ u8 erased_byte; /* value of erased bytes */

u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */
@@ -109,6 +127,7 @@ struct mmc_card {
struct mmc_csd csd; /* card specific */
struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
struct sd_scr scr; /* extra SD information */
+ struct sd_ssr ssr; /* yet more SD information */
struct sd_switch_caps sw_caps; /* switch (CMD6) caps */

unsigned int sdio_funcs; /* number of SDIO functions */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index e4898e9..7429033 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -92,6 +92,8 @@ struct mmc_command {
* actively failing requests
*/

+ unsigned int erase_timeout; /* in milliseconds */
+
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
};
@@ -134,6 +136,23 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);

+#define MMC_ERASE_ARG 0x00000000
+#define MMC_SECURE_ERASE_ARG 0x80000000
+#define MMC_TRIM_ARG 0x00000001
+#define MMC_SECURE_TRIM1_ARG 0x80000001
+#define MMC_SECURE_TRIM2_ARG 0x80008000
+
+#define MMC_SECURE_ARGS 0x80000000
+#define MMC_TRIM_ARGS 0x00008001
+
+extern int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg);
+extern int mmc_can_erase(struct mmc_card *card);
+extern int mmc_can_trim(struct mmc_card *card);
+extern int mmc_can_secure_erase_trim(struct mmc_card *card);
+extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
+ unsigned int nr);
+
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f65913c..3beb29d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -155,6 +155,7 @@ struct mmc_host {
#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
+#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */

mmc_pm_flag_t pm_caps; /* supported pm features */

diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 8a49cbf..82cdf8b 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -251,12 +251,20 @@ struct _mmc_csd {
* EXT_CSD fields
*/

-#define EXT_CSD_BUS_WIDTH 183 /* R/W */
-#define EXT_CSD_HS_TIMING 185 /* R/W */
-#define EXT_CSD_CARD_TYPE 196 /* RO */
-#define EXT_CSD_REV 192 /* RO */
-#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
-#define EXT_CSD_S_A_TIMEOUT 217
+#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
+#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+#define EXT_CSD_S_A_TIMEOUT 217 /* RO */
+#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */
+#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */
+#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */
+#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */
+#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
+#define EXT_CSD_TRIM_MULT 232 /* RO */

/*
* EXT_CSD field definitions
@@ -274,6 +282,10 @@ struct _mmc_csd {
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */

+#define EXT_CSD_SEC_ER_EN BIT(0)
+#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
+#define EXT_CSD_SEC_GB_CL_EN BIT(4)
+
/*
* MMC_SWITCH access modes
*/
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index f310062..3fd85e0 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -21,8 +21,13 @@
/* 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_SD_STATUS 13 /* adtc 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 */
--
1.6.3.3

2010-06-24 08:44:54

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V3 3/5] omap_hsmmc: Add erase capability

>From 20b7223c8a3c021a31a7b8079990ec103d89007e Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Tue, 18 May 2010 17:33:13 +0300
Subject: [PATCH 3/5] omap_hsmmc: Add erase capability

Disable the data (busy) timeout for erases and set the
MMC_CAP_ERASE capability.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 13 ++++++++++---
1 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b032828..5cbbe09 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -28,6 +28,7 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
@@ -78,6 +79,7 @@
#define INT_EN_MASK 0x307F0033
#define BWR_ENABLE (1 << 4)
#define BRR_ENABLE (1 << 5)
+#define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
@@ -523,7 +525,8 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}

-static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
+static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
+ struct mmc_command *cmd)
{
unsigned int irq_mask;

@@ -532,6 +535,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
else
irq_mask = INT_EN_MASK;

+ /* Disable timeout for erases */
+ if (cmd->opcode == MMC_ERASE)
+ irq_mask &= ~DTO_ENABLE;
+
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
@@ -782,7 +789,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;

- omap_hsmmc_enable_irq(host);
+ omap_hsmmc_enable_irq(host, cmd);

host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -2094,7 +2101,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->max_seg_size = mmc->max_req_size;

mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_WAIT_WHILE_BUSY;
+ MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;

if (mmc_slot(host).wires >= 8)
mmc->caps |= MMC_CAP_8_BIT_DATA;
--
1.6.3.3

2010-06-24 08:45:13

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V3 4/5] block: Add secure discard

>From b25b9a499f255ee5999c219525d82ef40382318c Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 23 Jun 2010 15:41:38 +0300
Subject: [PATCH 4/5] block: Add secure discard

Secure discard is the same as discard except that all copies
of the discarded sectors (perhaps created by garbage collection)
must also be erased.

Signed-off-by: Adrian Hunter <[email protected]>
---
block/blk-core.c | 6 +++++-
block/blk-lib.c | 6 ++++++
block/compat_ioctl.c | 1 +
block/elevator.c | 4 ++++
block/ioctl.c | 15 ++++++++++-----
include/linux/bio.h | 7 ++++++-
include/linux/blkdev.h | 10 +++++++++-
include/linux/fs.h | 2 ++
kernel/trace/blktrace.c | 8 ++++++++
9 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index f84cce4..6f9c6df 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1153,6 +1153,8 @@ void init_request_from_bio(struct request *req, struct bio *bio)
req->cmd_flags |= REQ_DISCARD;
if (bio_rw_flagged(bio, BIO_RW_BARRIER))
req->cmd_flags |= REQ_SOFTBARRIER;
+ if (bio_rw_flagged(bio, BIO_RW_SECURE))
+ req->cmd_flags |= REQ_SECURE;
} else if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER)))
req->cmd_flags |= REQ_HARDBARRIER;

@@ -1501,7 +1503,9 @@ static inline void __generic_make_request(struct bio *bio)
goto end_io;

if (bio_rw_flagged(bio, BIO_RW_DISCARD) &&
- !blk_queue_discard(q)) {
+ (!blk_queue_discard(q) ||
+ (bio_rw_flagged(bio, BIO_RW_SECURE) &&
+ !blk_queue_secdiscard(q)))) {
err = -EOPNOTSUPP;
goto end_io;
}
diff --git a/block/blk-lib.c b/block/blk-lib.c
index d0216b9..dc56682 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -52,6 +52,12 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
if (!blk_queue_discard(q))
return -EOPNOTSUPP;

+ if (flags & BLKDEV_IFL_SECURE) {
+ if (!blk_queue_secdiscard(q))
+ return -EOPNOTSUPP;
+ type |= DISCARD_SECURE;
+ }
+
while (nr_sects && !ret) {
unsigned int sector_size = q->limits.logical_block_size;
unsigned int max_discard_sectors =
diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c
index f26051f..24a146d 100644
--- a/block/compat_ioctl.c
+++ b/block/compat_ioctl.c
@@ -753,6 +753,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
case BLKFLSBUF:
case BLKROSET:
case BLKDISCARD:
+ case BLKSECDISCARD:
/*
* the ones below are implemented in blkdev_locked_ioctl,
* but we call blkdev_ioctl, which gets the lock for us
diff --git a/block/elevator.c b/block/elevator.c
index 923a913..23b5f3d 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -83,6 +83,10 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio)
bio_rw_flagged(rq->bio, BIO_RW_DISCARD))
return 0;

+ if (bio_rw_flagged(bio, BIO_RW_SECURE) !=
+ bio_rw_flagged(rq->bio, BIO_RW_SECURE))
+ return 0;
+
/*
* different data direction or already started, don't merge
*/
diff --git a/block/ioctl.c b/block/ioctl.c
index e8eb679..1fba55f 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -114,8 +114,10 @@ static int blkdev_reread_part(struct block_device *bdev)
}

static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
- uint64_t len)
+ uint64_t len, int secure)
{
+ unsigned long flags = BLKDEV_IFL_WAIT;
+
if (start & 511)
return -EINVAL;
if (len & 511)
@@ -125,8 +127,9 @@ static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,

if (start + len > (bdev->bd_inode->i_size >> 9))
return -EINVAL;
- return blkdev_issue_discard(bdev, start, len, GFP_KERNEL,
- BLKDEV_IFL_WAIT);
+ if (secure)
+ flags |= BLKDEV_IFL_SECURE;
+ return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
}

static int put_ushort(unsigned long arg, unsigned short val)
@@ -226,7 +229,8 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
unlock_kernel();
return 0;

- case BLKDISCARD: {
+ case BLKDISCARD:
+ case BLKSECDISCARD: {
uint64_t range[2];

if (!(mode & FMODE_WRITE))
@@ -235,7 +239,8 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
if (copy_from_user(range, (void __user *)arg, sizeof(range)))
return -EFAULT;

- return blk_ioctl_discard(bdev, range[0], range[1]);
+ return blk_ioctl_discard(bdev, range[0], range[1],
+ cmd == BLKSECDISCARD);
}

case HDIO_GETGEO: {
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 7fc5606..5355b42 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -160,7 +160,11 @@ struct bio {
* for flash based storage.
* Don't want driver retries for any fast fail whatever the reason.
* bit 10 -- Tell the IO scheduler not to wait for more requests after this
- one has been submitted, even if it is a SYNC request.
+ * one has been submitted, even if it is a SYNC request.
+ * bit 11 -- secure
+ * Used with BIO_RW_DISCARD to indicate a secure discard, which means
+ * all copies of the discarded sectors (perhaps created by garbage
+ * collection) are also erased.
*/
enum bio_rw_flags {
BIO_RW,
@@ -175,6 +179,7 @@ enum bio_rw_flags {
BIO_RW_META,
BIO_RW_DISCARD,
BIO_RW_NOIDLE,
+ BIO_RW_SECURE,
};

/*
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 09a8402..cff69d3 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -115,6 +115,7 @@ enum rq_flag_bits {
__REQ_NOIDLE, /* Don't anticipate more IO after this one */
__REQ_IO_STAT, /* account I/O stat */
__REQ_MIXED_MERGE, /* merge of different types, fail separately */
+ __REQ_SECURE, /* secure discard (used with __REQ_DISCARD) */
__REQ_NR_BITS, /* stops here */
};

@@ -144,6 +145,7 @@ enum rq_flag_bits {
#define REQ_NOIDLE (1 << __REQ_NOIDLE)
#define REQ_IO_STAT (1 << __REQ_IO_STAT)
#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
+#define REQ_SECURE (1 << __REQ_SECURE)

#define REQ_FAILFAST_MASK (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | \
REQ_FAILFAST_DRIVER)
@@ -467,6 +469,7 @@ struct request_queue
#define QUEUE_FLAG_IO_STAT 15 /* do IO stats */
#define QUEUE_FLAG_DISCARD 16 /* supports DISCARD */
#define QUEUE_FLAG_NOXMERGES 17 /* No extended merges */
+#define QUEUE_FLAG_SECDISCARD 18 /* supports SECDISCARD */

#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_CLUSTER) | \
@@ -600,6 +603,8 @@ enum {
#define blk_queue_stackable(q) \
test_bit(QUEUE_FLAG_STACKABLE, &(q)->queue_flags)
#define blk_queue_discard(q) test_bit(QUEUE_FLAG_DISCARD, &(q)->queue_flags)
+#define blk_queue_secdiscard(q) (blk_queue_discard(q) && \
+ test_bit(QUEUE_FLAG_SECDISCARD, &(q)->queue_flags))

#define blk_fs_request(rq) ((rq)->cmd_type == REQ_TYPE_FS)
#define blk_pc_request(rq) ((rq)->cmd_type == REQ_TYPE_BLOCK_PC)
@@ -628,6 +633,7 @@ enum {
#define blk_barrier_rq(rq) ((rq)->cmd_flags & REQ_HARDBARRIER)
#define blk_fua_rq(rq) ((rq)->cmd_flags & REQ_FUA)
#define blk_discard_rq(rq) ((rq)->cmd_flags & REQ_DISCARD)
+#define blk_secure_rq(rq) ((rq)->cmd_flags & REQ_SECURE)
#define blk_bidi_rq(rq) ((rq)->next_rq != NULL)
/* rq->queuelist of dequeued request must be list_empty() */
#define blk_queued_rq(rq) (!list_empty(&(rq)->queuelist))
@@ -1005,10 +1011,12 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt,
}
enum{
BLKDEV_WAIT, /* wait for completion */
- BLKDEV_BARRIER, /*issue request with barrier */
+ BLKDEV_BARRIER, /* issue request with barrier */
+ BLKDEV_SECURE, /* secure discard */
};
#define BLKDEV_IFL_WAIT (1 << BLKDEV_WAIT)
#define BLKDEV_IFL_BARRIER (1 << BLKDEV_BARRIER)
+#define BLKDEV_IFL_SECURE (1 << BLKDEV_SECURE)
extern int blkdev_issue_flush(struct block_device *, gfp_t, sector_t *,
unsigned long);
extern int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 471e1ff..03b6e88 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -167,6 +167,7 @@ struct inodes_stat_t {
*/
#define DISCARD_NOBARRIER (WRITE | (1 << BIO_RW_DISCARD))
#define DISCARD_BARRIER (DISCARD_NOBARRIER | (1 << BIO_RW_BARRIER))
+#define DISCARD_SECURE (DISCARD_NOBARRIER | (1 << BIO_RW_SECURE))

#define SEL_IN 1
#define SEL_OUT 2
@@ -309,6 +310,7 @@ struct inodes_stat_t {
#define BLKALIGNOFF _IO(0x12,122)
#define BLKPBSZGET _IO(0x12,123)
#define BLKDISCARDZEROES _IO(0x12,124)
+#define BLKSECDISCARD _IO(0x12,125)

#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
#define FIBMAP _IO(0x00,1) /* bmap access */
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index 638711c..1391ba3 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -664,6 +664,9 @@ static void blk_add_trace_rq(struct request_queue *q, struct request *rq,
if (blk_discard_rq(rq))
rw |= (1 << BIO_RW_DISCARD);

+ if (blk_secure_rq(rq))
+ rw |= (1 << BIO_RW_SECURE);
+
if (blk_pc_request(rq)) {
what |= BLK_TC_ACT(BLK_TC_PC);
__blk_add_trace(bt, 0, blk_rq_bytes(rq), rw,
@@ -1770,6 +1773,8 @@ void blk_fill_rwbs(char *rwbs, u32 rw, int bytes)
rwbs[i++] = 'S';
if (rw & 1 << BIO_RW_META)
rwbs[i++] = 'M';
+ if (rw & 1 << BIO_RW_SECURE)
+ rwbs[i++] = 'E';

rwbs[i] = '\0';
}
@@ -1782,6 +1787,9 @@ void blk_fill_rwbs_rq(char *rwbs, struct request *rq)
if (blk_discard_rq(rq))
rw |= (1 << BIO_RW_DISCARD);

+ if (blk_secure_rq(rq))
+ rw |= (1 << BIO_RW_SECURE);
+
bytes = blk_rq_bytes(rq);

blk_fill_rwbs(rwbs, rw, bytes);
--
1.6.3.3

2010-06-24 08:45:31

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V3 2/5] mmc_block: Add discard support

>From 1c859151d2d8c76e503a24c6d3d472cc01a68b3a Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Thu, 3 Jun 2010 10:47:12 +0300
Subject: [PATCH 2/5] mmc_block: Add discard support

Enable MMC to service discard requests. In the case of SD
and MMC cards that do not support trim, discards become
erases. In the case of cards (MMC) that only allow erases
in multiples of erase group size, round to the nearest
completely discarded erase group.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/card/block.c | 42 +++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/card/queue.c | 16 ++++++++++++++--
2 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index cb9fbc8..af14ae1 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -242,7 +242,40 @@ static u32 get_card_status(struct mmc_card *card, struct request *req)
return cmd.resp[0];
}

-static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ mmc_claim_host(card->host);
+
+ if (!mmc_can_erase(card)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card))
+ arg = MMC_TRIM_ARG;
+ else
+ arg = MMC_ERASE_ARG;
+
+ err = mmc_erase(card, from, nr, arg);
+out:
+ spin_lock_irq(&md->lock);
+ __blk_end_request(req, err, blk_rq_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ mmc_release_host(card->host);
+
+ return err ? 0 : 1;
+}
+
+static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
@@ -470,6 +503,13 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
return 0;
}

+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+ if (blk_discard_rq(req))
+ return mmc_blk_issue_discard_rq(mq, req);
+ else
+ return mmc_blk_issue_rw_rq(mq, req);
+}

static inline int mmc_blk_readonly(struct mmc_card *card)
{
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d6ded24..37f648f 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -30,9 +30,9 @@
static int mmc_prep_request(struct request_queue *q, struct request *req)
{
/*
- * We only like normal block requests.
+ * We only like normal block requests and discards.
*/
- if (!blk_fs_request(req)) {
+ if (!blk_fs_request(req) && !blk_discard_rq(req)) {
blk_dump_rq_flags(req, "MMC bad request");
return BLKPREP_KILL;
}
@@ -130,6 +130,18 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
blk_queue_prep_rq(mq->queue, mmc_prep_request);
blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
+ if (mmc_can_erase(card)) {
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mq->queue);
+ mq->queue->limits.max_discard_sectors = UINT_MAX;
+ if (card->erased_byte == 0)
+ mq->queue->limits.discard_zeroes_data = 1;
+ if (!mmc_can_trim(card) && is_power_of_2(card->erase_size)) {
+ mq->queue->limits.discard_granularity =
+ card->erase_size << 9;
+ mq->queue->limits.discard_alignment =
+ card->erase_size << 9;
+ }
+ }

#ifdef CONFIG_MMC_BLOCK_BOUNCE
if (host->max_hw_segs == 1) {
--
1.6.3.3

2010-06-24 08:45:49

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V3 5/5] mmc_block: Add support for secure discard

>From 3104475071c1f4959912a1529b179c912cbfb56f Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 23 Jun 2010 15:55:50 +0300
Subject: [PATCH 5/5] mmc_block: Add support for secure discard

Secure discard is implemented by Secure Trim if the discard
is unaligned or Secure Erase otherwise.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/card/block.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/card/queue.c | 3 +++
2 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index af14ae1..2e96ae5 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -275,6 +275,42 @@ out:
return err ? 0 : 1;
}

+static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ mmc_claim_host(card->host);
+
+ if (!mmc_can_secure_erase_trim(card)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+ arg = MMC_SECURE_TRIM1_ARG;
+ else
+ arg = MMC_SECURE_ERASE_ARG;
+
+ err = mmc_erase(card, from, nr, arg);
+ if (!err && arg == MMC_SECURE_TRIM1_ARG)
+ err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
+out:
+ spin_lock_irq(&md->lock);
+ __blk_end_request(req, err, blk_rq_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ mmc_release_host(card->host);
+
+ return err ? 0 : 1;
+}
+
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
@@ -505,10 +541,14 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)

static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
- if (blk_discard_rq(req))
- return mmc_blk_issue_discard_rq(mq, req);
- else
+ if (blk_discard_rq(req)) {
+ if (blk_secure_rq(req))
+ return mmc_blk_issue_secdiscard_rq(mq, req);
+ else
+ return mmc_blk_issue_discard_rq(mq, req);
+ } else {
return mmc_blk_issue_rw_rq(mq, req);
+ }
}

static inline int mmc_blk_readonly(struct mmc_card *card)
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 37f648f..b1ac755 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -141,6 +141,9 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
mq->queue->limits.discard_alignment =
card->erase_size << 9;
}
+ if (mmc_can_secure_erase_trim(card))
+ queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD,
+ mq->queue);
}

#ifdef CONFIG_MMC_BLOCK_BOUNCE
--
1.6.3.3

2010-06-30 22:10:46

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH V3 4/5] block: Add secure discard

On Thu, 24 Jun 2010 11:44:23 +0300
Adrian Hunter <[email protected]> wrote:

> >From b25b9a499f255ee5999c219525d82ef40382318c Mon Sep 17 00:00:00 2001
> From: Adrian Hunter <[email protected]>
> Date: Wed, 23 Jun 2010 15:41:38 +0300
> Subject: [PATCH 4/5] block: Add secure discard
>
> Secure discard is the same as discard except that all copies
> of the discarded sectors (perhaps created by garbage collection)
> must also be erased.

That's not an awfully informative changelog.

>From a quick peek at the code it seems that you took your earlier
design sketch:

On Mon, Jun 14, 2010 at 02:10:06PM +0300, Adrian Hunter wrote:
> Needs a bio flag, a request flag, setup the request flag based on the
> bio flag, prevent merging secure and non-secure discards, prevent drivers
> doing non-secure discards for secure discards.
>
> Seems like a lot of little changes for something that no one wants.
> Shouldn't it wait for someone to need it first?

and changed your mind and implemented it.

Is that a correct interpretation?

2010-06-30 22:22:37

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH V3 1/5] mmc: Add erase, secure erase, trim and secure trim operations

On Thu, 24 Jun 2010 11:44:00 +0300
Adrian Hunter <[email protected]> wrote:

> SD/MMC cards tend to support an erase operation. In addition,
> eMMC v4.4 cards can support secure erase, trim and secure trim
> operations that are all variants of the basic erase command.

The patch proposes a new userspace interface via sysfs, yes?

Please fully describe that interface and its operation in the
changelog. It'd also be nice to add permanent documentation for it.

>From reading the code, it appears that erase_size and
preferred_erase_size have units in bytes. But users shouldn't need to
read the code to find that out. What are the alignemnt and size
requirements on these? What is their position in /sys? What do they
actually *do* and what is the difference between them?

etetera. People want to review this code and other people actually
want to use it. I'm not sure that I want to try to review this code
when nobody's told me what interface it implements and how it's
supposed to work. Seems that whoever implemented BLKDISCARD didn't
want anyone to use it either. Sigh.


All of mmc core appears to use 32-bit quantities to represent sectors,
yes? Why didn't it use sector_t? What are the implications of this?

2010-07-01 10:19:08

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH V3 4/5] block: Add secure discard

Andrew Morton wrote:
> On Thu, 24 Jun 2010 11:44:23 +0300
> Adrian Hunter <[email protected]> wrote:
>
>> >From b25b9a499f255ee5999c219525d82ef40382318c Mon Sep 17 00:00:00 2001
>> From: Adrian Hunter <[email protected]>
>> Date: Wed, 23 Jun 2010 15:41:38 +0300
>> Subject: [PATCH 4/5] block: Add secure discard
>>
>> Secure discard is the same as discard except that all copies
>> of the discarded sectors (perhaps created by garbage collection)
>> must also be erased.
>
> That's not an awfully informative changelog.
>
>>From a quick peek at the code it seems that you took your earlier
> design sketch:
>
> On Mon, Jun 14, 2010 at 02:10:06PM +0300, Adrian Hunter wrote:
>> Needs a bio flag, a request flag, setup the request flag based on the
>> bio flag, prevent merging secure and non-secure discards, prevent drivers
>> doing non-secure discards for secure discards.
>>
>> Seems like a lot of little changes for something that no one wants.
>> Shouldn't it wait for someone to need it first?
>
> and changed your mind and implemented it.
>
> Is that a correct interpretation?
>

Yes. It does allude to that in "[PATCH V3 0/5] Add MMC erase and secure erase V3"
i.e.

Changes from V2

- move the addition of BLKSECDISCARD to a separate patch and implement it
using I/O requests
- move the MMC support of secure discard to a separate patch and support
the secure discard I/O request

2010-07-01 10:50:31

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH V3 1/5] mmc: Add erase, secure erase, trim and secure trim operations

Andrew Morton wrote:
> On Thu, 24 Jun 2010 11:44:00 +0300
> Adrian Hunter <[email protected]> wrote:
>
>> SD/MMC cards tend to support an erase operation. In addition,
>> eMMC v4.4 cards can support secure erase, trim and secure trim
>> operations that are all variants of the basic erase command.
>
> The patch proposes a new userspace interface via sysfs, yes?
,
Just two read-only values

>
> Please fully describe that interface and its operation in the
> changelog. It'd also be nice to add permanent documentation for it.
>

OK

>>From reading the code, it appears that erase_size and
> preferred_erase_size have units in bytes. But users shouldn't need to
> read the code to find that out. What are the alignemnt and size
> requirements on these? What is their position in /sys? What do they
> actually *do* and what is the difference between them?
>
> etetera. People want to review this code and other people actually
> want to use it. I'm not sure that I want to try to review this code
> when nobody's told me what interface it implements and how it's
> supposed to work. Seems that whoever implemented BLKDISCARD didn't
> want anyone to use it either. Sigh.
>
>
> All of mmc core appears to use 32-bit quantities to represent sectors,
> yes? Why didn't it use sector_t? What are the implications of this?

SD/MMC addressing uses 32-bit values. There is a known 2TB limit for
SD/MMC cards. As cards are only just getting to 64GB, that limit is
some way off, and it is not clear NAND technology can get there in
a SD/MMC package anyway.

I don't know why sector_t is not used. I guess it would complicate doing
division since it can be 64-bit.

The implications are minimal. In the unlikely event SD/MMC cards ever
exceed 2TB some changes will be needed, but the standard would have to
change to allow that first.