- If available, use TRIM instead of ERASE for implementing BIO_DELETE.

This also involves adding a quirk table as TRIM is broken for some
  Kingston eMMC devices, though. Compared to ERASE (declared "legacy"
  in the eMMC specification v5.1), TRIM has the advantage of operating
  on write sectors rather than on erase sectors, which typically are
  of a much larger size. Thus, employing TRIM, we don't need to fiddle
  with coalescing BIO_DELETE requests that are also of (write) sector
  units into erase sectors, which might not even add up in all cases.
- For some SanDisk iNAND devices, the CMD38 argument, e. g. ERASE,
  TRIM etc., has to be specified via EXT_CSD[113], which now is also
  handled via a quirk.
- My initial understanding was that for eMMC partitions, the granularity
  should be used as erase sector size, e. g. 128 KB for boot partitions.
  However, rereading the relevant parts of the eMMC specification v5.1,
  this isn't actually correct. So drop the code which used partition
  granularities for delmaxsize and stripesize. For the most part, this
  change is a NOP, though, because a) for ERASE, mmcsd_delete() used
  the erase sector size unconditionally for all partitions anyway and
  b) g_disk_limit() doesn't actually take the stripesize into account.
- Take some more advantage of mmcsd_errmsg() in mmcsd(4) for making
  error codes human readable.
This commit is contained in:
marius 2017-08-07 23:33:05 +00:00
parent be20038c69
commit 19310c915c
5 changed files with 189 additions and 53 deletions

View File

@ -180,7 +180,7 @@ struct mmc_host {
extern driver_t mmc_driver;
extern devclass_t mmc_devclass;
#define MMC_VERSION 4
#define MMC_VERSION 5
#define MMC_DECLARE_BRIDGE(name) \
DRIVER_MODULE(mmc, name, mmc_driver, mmc_devclass, NULL, NULL); \

View File

@ -104,12 +104,34 @@ struct mmc_ivars {
uint32_t hs_tran_speed; /* Max speed in high speed mode */
uint32_t erase_sector; /* Card native erase sector size */
uint32_t cmd6_time; /* Generic switch timeout [us] */
uint32_t quirks; /* Quirks as per mmc_quirk->quirks */
char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */
char card_sn_string[16];/* Formatted serial # for disk->d_ident */
};
#define CMD_RETRIES 3
static const struct mmc_quirk mmc_quirks[] = {
/*
* For some SanDisk iNAND devices, the CMD38 argument needs to be
* provided in EXT_CSD[113].
*/
{ 0x2, 0x100, "SEM02G", MMC_QUIRK_INAND_CMD38 },
{ 0x2, 0x100, "SEM04G", MMC_QUIRK_INAND_CMD38 },
{ 0x2, 0x100, "SEM08G", MMC_QUIRK_INAND_CMD38 },
{ 0x2, 0x100, "SEM16G", MMC_QUIRK_INAND_CMD38 },
{ 0x2, 0x100, "SEM32G", MMC_QUIRK_INAND_CMD38 },
/*
* Disable TRIM for Kingston eMMCs where a firmware bug can lead to
* unrecoverable data corruption.
*/
{ 0x70, MMC_QUIRK_OID_ANY, "V10008", MMC_QUIRK_BROKEN_TRIM },
{ 0x70, MMC_QUIRK_OID_ANY, "V10016", MMC_QUIRK_BROKEN_TRIM },
{ 0x0, 0x0, NULL, 0x0 }
};
static SYSCTL_NODE(_hw, OID_AUTO, mmc, CTLFLAG_RD, NULL, "mmc driver");
static int mmc_debug;
@ -1109,7 +1131,7 @@ mmc_format_card_id_string(struct mmc_ivars *ivar)
/*
* Format a card ID string for use by the mmcsd driver, it's what
* appears between the <> in the following:
* mmcsd0: 968MB <SD SD01G 8.0 SN 2686905 Mfg 08/2008 by 3 TN> at mmc0
* mmcsd0: 968MB <SD SD01G 8.0 SN 2686905 MFG 08/2008 by 3 TN> at mmc0
* 22.5MHz/4bit/128-block
*
* Also format just the card serial number, which the mmcsd driver will
@ -1547,6 +1569,7 @@ mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard)
break;
}
}
device_printf(dev, " quirks: %b\n", ivar->quirks, MMC_QUIRKS_FMT);
device_printf(dev, " bus: %ubit, %uMHz (%s timing)\n",
(ivar->bus_width == bus_width_1 ? 1 :
(ivar->bus_width == bus_width_4 ? 4 : 8)),
@ -1563,6 +1586,7 @@ mmc_discover_cards(struct mmc_softc *sc)
u_char switch_res[64];
uint32_t raw_cid[4];
struct mmc_ivars *ivar = NULL;
const struct mmc_quirk *quirk;
device_t child;
int err, host_caps, i, newcard;
uint32_t resp, sec_count, status;
@ -1870,6 +1894,18 @@ mmc_discover_cards(struct mmc_softc *sc)
ivar->raw_ext_csd[EXT_CSD_REV] >= 5);
child_common:
for (quirk = &mmc_quirks[0]; quirk->mid != 0x0; quirk++) {
if ((quirk->mid == MMC_QUIRK_MID_ANY ||
quirk->mid == ivar->cid.mid) &&
(quirk->oid == MMC_QUIRK_OID_ANY ||
quirk->oid == ivar->cid.oid) &&
strncmp(quirk->pnm, ivar->cid.pnm,
sizeof(ivar->cid.pnm)) == 0) {
ivar->quirks = quirk->quirks;
break;
}
}
/*
* Some cards that report maximum I/O block sizes greater
* than 512 require the block length to be set to 512, even
@ -2471,6 +2507,12 @@ mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
case MMC_IVAR_MAX_DATA:
*result = mmcbr_get_max_data(bus);
break;
case MMC_IVAR_CMD6_TIMEOUT:
*result = ivar->cmd6_time;
break;
case MMC_IVAR_QUIRKS:
*result = ivar->quirks;
break;
case MMC_IVAR_CARD_ID_STRING:
*(char **)result = ivar->card_id_string;
break;

View File

@ -268,6 +268,13 @@ struct mmc_request {
#define MMC_ERASE_GROUP_END 36
/* 37 -- reserved old command */
#define MMC_ERASE 38
#define MMC_ERASE_ERASE 0x00000000
#define MMC_ERASE_TRIM 0x00000001
#define MMC_ERASE_FULE 0x00000002
#define MMC_ERASE_DISCARD 0x00000003
#define MMC_ERASE_SECURE_ERASE 0x80000000
#define MMC_ERASE_SECURE_TRIM1 0x80000001
#define MMC_ERASE_SECURE_TRIM2 0x80008000
/* Class 9: I/O mode commands */
#define MMC_FAST_IO 39
@ -375,6 +382,7 @@ struct mmc_request {
#define EXT_CSD_ERASE_TO_MULT 223 /* RO */
#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */
#define EXT_CSD_BOOT_SIZE_MULT 226 /* RO */
#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
#define EXT_CSD_PWR_CL_200_195 236 /* RO */
#define EXT_CSD_PWR_CL_200_360 237 /* RO */
#define EXT_CSD_PWR_CL_52_195_DDR 238 /* RO */
@ -459,6 +467,22 @@ struct mmc_request {
#define EXT_CSD_STROBE_SUPPORT_EN 0x01
#define EXT_CSD_SEC_FEATURE_SUPPORT_ER_EN 0x01
#define EXT_CSD_SEC_FEATURE_SUPPORT_BD_BLK_EN 0x04
#define EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN 0x10
#define EXT_CSD_SEC_FEATURE_SUPPORT_SANITIZE 0x40
/*
* Vendor specific EXT_CSD fields
*/
/* SanDisk iNAND */
#define EXT_CSD_INAND_CMD38 113
#define EXT_CSD_INAND_CMD38_ERASE 0x00
#define EXT_CSD_INAND_CMD38_TRIM 0x01
#define EXT_CSD_INAND_CMD38_SECURE_ERASE 0x80
#define EXT_CSD_INAND_CMD38_SECURE_TRIM1 0x81
#define EXT_CSD_INAND_CMD38_SECURE_TRIM2 0x82
#define MMC_TYPE_HS_26_MAX 26000000
#define MMC_TYPE_HS_52_MAX 52000000
#define MMC_TYPE_DDR52_MAX 52000000
@ -600,8 +624,7 @@ struct mmc_cid {
uint8_t fwrev;
};
struct mmc_csd
{
struct mmc_csd {
uint8_t csd_structure;
uint8_t spec_vers;
uint16_t ccc;
@ -627,16 +650,14 @@ struct mmc_csd
wp_grp_enable:1;
};
struct mmc_scr
{
struct mmc_scr {
unsigned char sda_vsn;
unsigned char bus_widths;
#define SD_SCR_BUS_WIDTH_1 (1 << 0)
#define SD_SCR_BUS_WIDTH_4 (1 << 2)
};
struct mmc_sd_status
{
struct mmc_sd_status {
uint8_t bus_width;
uint8_t secured_mode;
uint16_t card_type;
@ -649,6 +670,19 @@ struct mmc_sd_status
uint8_t erase_offset;
};
struct mmc_quirk {
uint32_t mid;
#define MMC_QUIRK_MID_ANY ((uint32_t)-1)
uint16_t oid;
#define MMC_QUIRK_OID_ANY ((uint16_t)-1)
const char *pnm;
uint32_t quirks;
#define MMC_QUIRK_INAND_CMD38 0x0001
#define MMC_QUIRK_BROKEN_TRIM 0x0002
};
#define MMC_QUIRKS_FMT "\020" "\001INAND_CMD38" "\002BROKEN_TRIM"
/*
* Various MMC/SD constants
*/

View File

@ -126,6 +126,10 @@ struct mmcsd_softc {
uint8_t part_curr; /* Partition currently switched to */
uint8_t ext_csd[MMC_EXTCSD_SIZE];
uint16_t rca;
uint32_t flags;
#define MMCSD_INAND_CMD38 0x0001
#define MMCSD_USE_TRIM 0x0002
uint32_t cmd6_time; /* Generic switch timeout [us] */
uint32_t part_time; /* Partition switch timeout [us] */
off_t enh_base; /* Enhanced user data area slice base ... */
off_t enh_size; /* ... and size [bytes] */
@ -168,9 +172,10 @@ static int mmcsd_ioctl_rpmb(struct cdev *dev, u_long cmd, caddr_t data,
int fflag, struct thread *td);
static void mmcsd_add_part(struct mmcsd_softc *sc, u_int type,
const char *name, u_int cnt, off_t media_size, off_t erase_size, bool ro);
const char *name, u_int cnt, off_t media_size, bool ro);
static int mmcsd_bus_bit_width(device_t dev);
static daddr_t mmcsd_delete(struct mmcsd_part *part, struct bio *bp);
static const char *mmcsd_errmsg(int e);
static int mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data,
int fflag);
static int mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic,
@ -221,6 +226,7 @@ mmcsd_attach(device_t dev)
off_t erase_size, sector_size, size, wp_size;
uintmax_t bytes;
int err, i;
uint32_t quirks;
uint8_t rev;
bool comp, ro;
char unit[2];
@ -239,20 +245,47 @@ mmcsd_attach(device_t dev)
* place either.
*/
sc->max_data = mmc_get_max_data(dev);
sc->erase_sector = mmc_get_erase_sector(dev);
sc->high_cap = mmc_get_high_cap(dev);
sc->rca = mmc_get_rca(dev);
sc->cmd6_time = mmc_get_cmd6_timeout(dev);
quirks = mmc_get_quirks(dev);
/* Only MMC >= 4.x devices support EXT_CSD. */
if (mmc_get_spec_vers(dev) >= 4) {
MMCBUS_ACQUIRE_BUS(mmcbus, dev);
err = mmc_send_ext_csd(mmcbus, dev, sc->ext_csd);
MMCBUS_RELEASE_BUS(mmcbus, dev);
if (err != MMC_ERR_NONE)
bzero(sc->ext_csd, sizeof(sc->ext_csd));
if (err != MMC_ERR_NONE) {
device_printf(dev, "Error reading EXT_CSD %s\n",
mmcsd_errmsg(err));
return (ENXIO);
}
}
ext_csd = sc->ext_csd;
if ((quirks & MMC_QUIRK_INAND_CMD38) != 0) {
if (mmc_get_spec_vers(dev) < 4) {
device_printf(dev,
"MMC_QUIRK_INAND_CMD38 set but no EXT_CSD\n");
return (EINVAL);
}
sc->flags |= MMCSD_INAND_CMD38;
}
/*
* EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN denotes support for both
* insecure and secure TRIM.
*/
if ((ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] &
EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN) != 0 &&
(quirks & MMC_QUIRK_BROKEN_TRIM) == 0) {
if (bootverbose)
device_printf(dev, "taking advantage of TRIM\n");
sc->flags |= MMCSD_USE_TRIM;
sc->erase_sector = 1;
} else
sc->erase_sector = mmc_get_erase_sector(dev);
/*
* Enhanced user data area and general purpose partitions are only
* supported in revision 1.4 (EXT_CSD_REV == 4) and later, the RPMB
@ -306,8 +339,7 @@ mmcsd_attach(device_t dev)
*/
ro = mmc_get_read_only(dev);
mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_DEFAULT, "mmcsd",
device_get_unit(dev), mmc_get_media_size(dev) * sector_size,
sc->erase_sector * sector_size, ro);
device_get_unit(dev), mmc_get_media_size(dev) * sector_size, ro);
if (mmc_get_spec_vers(dev) < 3)
return (0);
@ -332,11 +364,11 @@ mmcsd_attach(device_t dev)
size = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
if (size > 0 && (mmcbr_get_caps(mmcbus) & MMC_CAP_BOOT_NOACC) == 0) {
mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_BOOT0,
MMCSD_FMT_BOOT, 0, size, MMC_BOOT_RPMB_BLOCK_SIZE,
MMCSD_FMT_BOOT, 0, size,
ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK) != 0));
mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_BOOT1,
MMCSD_FMT_BOOT, 1, size, MMC_BOOT_RPMB_BLOCK_SIZE,
MMCSD_FMT_BOOT, 1, size,
ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK) != 0));
}
@ -345,7 +377,7 @@ mmcsd_attach(device_t dev)
size = ext_csd[EXT_CSD_RPMB_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
if (rev >= 5 && size > 0)
mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_RPMB,
MMCSD_FMT_RPMB, 0, size, MMC_BOOT_RPMB_BLOCK_SIZE, ro);
MMCSD_FMT_RPMB, 0, size, ro);
if (rev <= 3 || comp == FALSE)
return (0);
@ -365,8 +397,7 @@ mmcsd_attach(device_t dev)
if (size == 0)
continue;
mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_GP0 + i,
MMCSD_FMT_GP, i, size * erase_size * wp_size,
erase_size, ro);
MMCSD_FMT_GP, i, size * erase_size * wp_size, ro);
}
}
return (0);
@ -419,7 +450,7 @@ static struct cdevsw mmcsd_rpmb_cdevsw = {
static void
mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt,
off_t media_size, off_t erase_size, bool ro)
off_t media_size, bool ro)
{
struct make_dev_args args;
device_t dev, mmcbus;
@ -482,10 +513,10 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt,
d->d_sectorsize = mmc_get_sector_size(dev);
d->d_maxsize = sc->max_data * d->d_sectorsize;
d->d_mediasize = media_size;
d->d_stripesize = erase_size;
d->d_stripesize = sc->erase_sector * d->d_sectorsize;
d->d_unit = cnt;
d->d_flags = DISKFLAG_CANDELETE;
d->d_delmaxsize = erase_size;
d->d_delmaxsize = mmc_get_erase_sector(dev) * d->d_sectorsize;
strlcpy(d->d_ident, mmc_get_card_sn_string(dev),
sizeof(d->d_ident));
strlcpy(d->d_descr, mmc_get_card_id_string(dev),
@ -1151,6 +1182,8 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp)
struct mmcsd_softc *sc;
device_t dev, mmcbus;
u_int erase_sector, sz;
int err;
bool use_trim;
sc = part->sc;
dev = sc->dev;
@ -1159,22 +1192,44 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp)
block = bp->bio_pblkno;
sz = part->disk->d_sectorsize;
end = bp->bio_pblkno + (bp->bio_bcount / sz);
/* Coalesce with part remaining from previous request. */
if (block > part->eblock && block <= part->eend)
block = part->eblock;
if (end >= part->eblock && end < part->eend)
end = part->eend;
/* Safe round to the erase sector boundaries. */
erase_sector = sc->erase_sector;
start = block + erase_sector - 1; /* Round up. */
start -= start % erase_sector;
stop = end; /* Round down. */
stop -= end % erase_sector;
/* We can't erase an area smaller than a sector, store it for later. */
if (start >= stop) {
part->eblock = block;
part->eend = end;
return (end);
use_trim = sc->flags & MMCSD_USE_TRIM;
if (use_trim == true) {
start = block;
stop = end;
} else {
/* Coalesce with the remainder of the previous request. */
if (block > part->eblock && block <= part->eend)
block = part->eblock;
if (end >= part->eblock && end < part->eend)
end = part->eend;
/* Safely round to the erase sector boundaries. */
erase_sector = sc->erase_sector;
start = block + erase_sector - 1; /* Round up. */
start -= start % erase_sector;
stop = end; /* Round down. */
stop -= end % erase_sector;
/*
* We can't erase an area smaller than an erase sector, so
* store it for later.
*/
if (start >= stop) {
part->eblock = block;
part->eend = end;
return (end);
}
}
if ((sc->flags & MMCSD_INAND_CMD38) != 0) {
err = mmc_switch(mmcbus, dev, sc->rca, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_INAND_CMD38, use_trim == true ?
EXT_CSD_INAND_CMD38_TRIM : EXT_CSD_INAND_CMD38_ERASE,
sc->cmd6_time, true);
if (err != MMC_ERR_NONE) {
device_printf(dev,
"Setting iNAND erase command failed %s\n",
mmcsd_errmsg(err));
return (block);
}
}
/*
@ -1198,8 +1253,8 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp)
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
if (req.cmd->error != MMC_ERR_NONE) {
device_printf(dev, "Setting erase start position failed %d\n",
req.cmd->error);
device_printf(dev, "Setting erase start position failed %s\n",
mmcsd_errmsg(req.cmd->error));
block = bp->bio_pblkno;
goto unpause;
}
@ -1218,8 +1273,8 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp)
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
if (req.cmd->error != MMC_ERR_NONE) {
device_printf(dev, "Setting erase stop position failed %d\n",
req.cmd->error);
device_printf(dev, "Setting erase stop position failed %s\n",
mmcsd_errmsg(req.cmd->error));
block = bp->bio_pblkno;
goto unpause;
}
@ -1228,23 +1283,24 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp)
memset(&cmd, 0, sizeof(cmd));
req.cmd = &cmd;
cmd.opcode = MMC_ERASE;
cmd.arg = 0;
cmd.arg = use_trim == true ? MMC_ERASE_TRIM : MMC_ERASE_ERASE;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
if (req.cmd->error != MMC_ERR_NONE) {
device_printf(dev, "erase err3: %d\n", req.cmd->error);
device_printf(dev, "Issuing erase command failed %d\n",
req.cmd->error);
device_printf(dev, "Issuing erase command failed %s\n",
mmcsd_errmsg(req.cmd->error));
block = bp->bio_pblkno;
goto unpause;
}
/* Store one of remaining parts for the next call. */
if (bp->bio_pblkno >= part->eblock || block == start) {
part->eblock = stop; /* Predict next forward. */
part->eend = end;
} else {
part->eblock = block; /* Predict next backward. */
part->eend = start;
if (use_trim == false) {
/* Store one of the remaining parts for the next call. */
if (bp->bio_pblkno >= part->eblock || block == start) {
part->eblock = stop; /* Predict next forward. */
part->eend = end;
} else {
part->eblock = block; /* Predict next backward. */
part->eend = start;
}
}
block = end;
unpause:

View File

@ -68,6 +68,8 @@ enum mmc_device_ivars {
MMC_IVAR_BUS_WIDTH,
MMC_IVAR_ERASE_SECTOR,
MMC_IVAR_MAX_DATA,
MMC_IVAR_CMD6_TIMEOUT,
MMC_IVAR_QUIRKS,
MMC_IVAR_CARD_ID_STRING,
MMC_IVAR_CARD_SN_STRING,
};
@ -90,6 +92,8 @@ MMC_ACCESSOR(card_type, CARD_TYPE, int)
MMC_ACCESSOR(bus_width, BUS_WIDTH, int)
MMC_ACCESSOR(erase_sector, ERASE_SECTOR, int)
MMC_ACCESSOR(max_data, MAX_DATA, int)
MMC_ACCESSOR(cmd6_timeout, CMD6_TIMEOUT, u_int)
MMC_ACCESSOR(quirks, QUIRKS, u_int)
MMC_ACCESSOR(card_id_string, CARD_ID_STRING, const char *)
MMC_ACCESSOR(card_sn_string, CARD_SN_STRING, const char *)