lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4C0CA5F7.6060104@nokia.com>
Date:	Mon, 07 Jun 2010 10:55:35 +0300
From:	Adrian Hunter <adrian.hunter@...ia.com>
To:	Kyungmin Park <kmpark@...radead.org>
CC:	Andrew Morton <akpm@...ux-foundation.org>,
	"linux-mmc@...r.kernel.org" <linux-mmc@...r.kernel.org>,
	LKML <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH 2/4] mmc: Add erase, secure erase, trim and secure trim
 	operations

Kyungmin Park wrote:
> Hi,
> 
> After apply patches, I know that it's not integrated with filesystem
> discard request.
> Only ioctl interface is allowed to support trim.
> 
> okay I will see the how to integrate with filesystem.

There is not much to it.

It is something along the lines of:
	- set QUEUE_FLAG_DISCARD on the queue if the card supports trim
	- amend mmc_prep_request() to let the discard request through if the card supports trim
	- amend mmc_blk_issue_rq() to call mmc_erase(card, from, nr, MMC_TRIM_ARG) for discards

> 
> Thank you,
> Kyungmin Park
> 
> On Fri, Jun 4, 2010 at 7:07 AM, Kyungmin Park <kmpark@...radead.org> wrote:
>> Hi adrian,
>>
>> First thank you for your works. we have to implement it but you did.
>>
>> Before the code review, can you provide the results of trim effect?
>> I wonder how much we can gain performance from trim command.
>>
>> So please give any benchmark result based on vfat or btrfs. since it's
>> already implemented the discard request.
>>
>> Thank you,
>> Kyungmin Park
>>
>> On Fri, Jun 4, 2010 at 6:13 AM, Adrian Hunter <adrian.hunter@...ia.com> wrote:
>>> From 8f9a8227c528c3853ee9eef2209cefcf1616ebb3 Mon Sep 17 00:00:00 2001
>>> From: Adrian Hunter <adrian.hunter@...ia.com>
>>> Date: Tue, 1 Jun 2010 13:20:22 +0300
>>> Subject: [PATCH 2/4] mmc: Add erase, secure erase, trim and secure trim
>>> operations
>>>
>>> As SD and MMC cards have a NAND core, they can support an erase
>>> operation that is typically 10x to 100x faster than writing.
>>>
>>> In addition, eMMCv4.4 also offers:
>>>       o Secure Erase
>>>       o Trim
>>>       o Secure Trim
>>>
>>> The "secure" variants also ensure that any copies of the data
>>> (for example remnants of garbage collection) are also erased.
>>>
>>> "Trim" is the same as "erase" except that individual sectors
>>> can be erased instead of whole Erase Groups.
>>>
>>> The erase operation and its variants are not supported by
>>> default and drivers must set MMC_CAP_ERASE.  This is because
>>> the operation can take a long time and drivers that rely on
>>> polling the status may perform very badly.  Also drivers
>>> may need changes to support the very long erase timeouts.
>>>
>>> Signed-off-by: Adrian Hunter <adrian.hunter@...ia.com>
>>> ---
>>> drivers/mmc/core/core.c   |  330
>>> +++++++++++++++++++++++++++++++++++++++++++++
>>> drivers/mmc/core/core.h   |    2 +
>>> drivers/mmc/core/mmc.c    |   44 ++++++-
>>> drivers/mmc/core/sd.c     |   81 +++++++++++
>>> 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, 567 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 569e94d..2627147 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -1050,6 +1050,336 @@ 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, 'max_erase' is defined as a guide to upper layers
>>> +        * (i.e. the MMC Block driver) 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->max_erase = card->ssr.au;
>>> +               card->erase_shift = ffs(card->ssr.au) - 1;
>>> +       } else if (card->ext_csd.hc_erase_size) {
>>> +               card->max_erase = card->ext_csd.hc_erase_size;
>>> +       } else {
>>> +               sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >>
>>> 11;
>>> +               if (sz < 128)
>>> +                       card->max_erase = 512 * 1024 / 512;
>>> +               else if (sz < 512)
>>> +                       card->max_erase = 1024 * 1024 / 512;
>>> +               else if (sz < 1024)
>>> +                       card->max_erase = 2 * 1024 * 1024 / 512;
>>> +               else
>>> +                       card->max_erase = 4 * 1024 * 1024 / 512;
>>> +               if (card->max_erase < card->erase_size)
>>> +                       card->max_erase = card->erase_size;
>>> +               else {
>>> +                       sz = card->max_erase % card->erase_size;
>>> +                       if (sz)
>>> +                               card->max_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 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_ERASE_ARG || arg == MMC_SECURE_ERASE_ARG) {
>>> +               if (from % card->erase_size || nr % card->erase_size)
>>> +                       return -EINVAL;
>>> +       }
>>> +
>>> +       if (nr == 0)
>>> +               return 0;
>>> +
>>> +       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..9603720 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,12 @@ 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;
>>> +       csd->erase_size >>= 9;
>>> +
>>>        return 0;
>>> }
>>>
>>> @@ -247,8 +263,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] << 19;
>>> +       }
>>> +
>>> +       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 +298,7 @@ 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);
>>> 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 +310,7 @@ 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_fwrev.attr,
>>>        &dev_attr_hwrev.attr,
>>>        &dev_attr_manfid.attr,
>>> @@ -407,6 +447,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..fe6bc74 100644
>>> --- a/drivers/mmc/core/sd.c
>>> +++ b/drivers/mmc/core/sd.c
>>> @@ -119,6 +119,14 @@ 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 {
>>> +                       csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1;
>>> +                       csd->erase_size <<= csd->write_blkbits;
>>> +                       csd->erase_size >>= 9;
>>> +               }
>>>                break;
>>>        case 1:
>>>                /*
>>> @@ -147,6 +155,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 +163,8 @@ static int mmc_decode_csd(struct mmc_card *card)
>>>                return -EINVAL;
>>>        }
>>>
>>> +       card->erase_size = csd->erase_size;
>>> +
>>>        return 0;
>>> }
>>>
>>> @@ -179,10 +190,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 +358,7 @@ 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);
>>> 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,7 @@ 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_fwrev.attr,
>>>        &dev_attr_hwrev.attr,
>>>        &dev_attr_manfid.attr,
>>> @@ -442,6 +513,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..eff7a08 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            max_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
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>>> the body of a message to majordomo@...r.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ