- If present, take advantage of the R/W cache of eMMC revision 1.5 and

later devices. These caches work akin to the ones found in HDDs/SSDs
  that ada(4)/da(4) also enable if existent, but likewise increase the
  likelihood of data loss in case of a sudden power outage etc. On the
  other hand, write performance is up to twice as high for e. g. 1 GiB
  files depending on the actual chip and transfer mode employed.
  For maximum data integrity, the usage of eMMC caches can be disabled
  via the hw.mmcsd.cache tunable.
- Get rid of the NOP mmcsd_open().
This commit is contained in:
Marius Strobl 2018-05-15 21:15:09 +00:00
parent e388d638b1
commit 646fd30caf
2 changed files with 115 additions and 14 deletions

View File

@ -357,6 +357,8 @@ struct mmc_request {
/*
* EXT_CSD fields
*/
#define EXT_CSD_FLUSH_CACHE 32 /* W/E */
#define EXT_CSD_CACHE_CTRL 33 /* R/W/E */
#define EXT_CSD_EXT_PART_ATTR 52 /* R/W, 2 bytes */
#define EXT_CSD_ENH_START_ADDR 136 /* R/W, 4 bytes */
#define EXT_CSD_ENH_SIZE_MULT 140 /* R/W, 3 bytes */
@ -390,12 +392,19 @@ struct mmc_request {
#define EXT_CSD_PWR_CL_200_360 237 /* RO */
#define EXT_CSD_PWR_CL_52_195_DDR 238 /* RO */
#define EXT_CSD_PWR_CL_52_360_DDR 239 /* RO */
#define EXT_CSD_CACHE_FLUSH_POLICY 249 /* RO */
#define EXT_CSD_GEN_CMD6_TIME 248 /* RO */
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_PWR_CL_200_360_DDR 253 /* RO */
/*
* EXT_CSD field definitions
*/
#define EXT_CSD_FLUSH_CACHE_FLUSH 0x01
#define EXT_CSD_FLUSH_CACHE_BARRIER 0x02
#define EXT_CSD_CACHE_CTRL_CACHE_EN 0x01
#define EXT_CSD_EXT_PART_ATTR_DEFAULT 0x0
#define EXT_CSD_EXT_PART_ATTR_SYSTEMCODE 0x1
#define EXT_CSD_EXT_PART_ATTR_NPERSISTENT 0x2
@ -475,6 +484,8 @@ struct mmc_request {
#define EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN 0x10
#define EXT_CSD_SEC_FEATURE_SUPPORT_SANITIZE 0x40
#define EXT_CSD_CACHE_FLUSH_POLICY_FIFO 0x01
/*
* Vendor specific EXT_CSD fields
*/

View File

@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/slicer.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <geom/geom.h>
@ -132,6 +133,8 @@ struct mmcsd_softc {
uint32_t flags;
#define MMCSD_INAND_CMD38 0x0001
#define MMCSD_USE_TRIM 0x0002
#define MMCSD_FLUSH_CACHE 0x0004
#define MMCSD_DIRTY 0x0008
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 ... */
@ -152,12 +155,19 @@ static const char *errmsg[] =
"NO MEMORY"
};
static SYSCTL_NODE(_hw, OID_AUTO, mmcsd, CTLFLAG_RD, NULL, "mmcsd driver");
static int mmcsd_cache = 1;
SYSCTL_INT(_hw_mmcsd, OID_AUTO, cache, CTLFLAG_RDTUN, &mmcsd_cache, 0,
"Device R/W cache enabled if present");
#define LOG_PPS 5 /* Log no more than 5 errors per second. */
/* bus entry points */
static int mmcsd_attach(device_t dev);
static int mmcsd_detach(device_t dev);
static int mmcsd_probe(device_t dev);
static int mmcsd_shutdown(device_t dev);
/* disk routines */
static int mmcsd_close(struct disk *dp);
@ -166,7 +176,6 @@ static int mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
static int mmcsd_getattr(struct bio *);
static int mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data,
int fflag, struct thread *td);
static int mmcsd_open(struct disk *dp);
static void mmcsd_strategy(struct bio *bp);
static void mmcsd_task(void *arg);
@ -179,6 +188,7 @@ static void mmcsd_add_part(struct mmcsd_softc *sc, u_int type,
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_flush_cache(struct mmcsd_softc *sc);
static int mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data,
int fflag, struct thread *td);
static int mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic,
@ -296,6 +306,31 @@ mmcsd_attach(device_t dev)
*/
rev = ext_csd[EXT_CSD_REV];
/*
* With revision 1.5 (MMC v4.5, EXT_CSD_REV == 6) and later, take
* advantage of the device R/W cache if present and useage is not
* disabled.
*/
if (rev >= 6 && mmcsd_cache != 0) {
size = ext_csd[EXT_CSD_CACHE_SIZE] |
ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 |
ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24;
if (bootverbose)
device_printf(dev, "cache size %juKB\n", size);
if (size > 0) {
MMCBUS_ACQUIRE_BUS(mmcbus, dev);
err = mmc_switch(mmcbus, dev, sc->rca,
EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL,
EXT_CSD_CACHE_CTRL_CACHE_EN, sc->cmd6_time, true);
MMCBUS_RELEASE_BUS(mmcbus, dev);
if (err != MMC_ERR_NONE)
device_printf(dev, "failed to enable cache\n");
else
sc->flags |= MMCSD_FLUSH_CACHE;
}
}
/*
* Ignore user-creatable enhanced user data area and general purpose
* partitions partitions as long as partitioning hasn't been finished.
@ -505,7 +540,6 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt,
MMCSD_DISK_LOCK_INIT(part);
d = part->disk = disk_alloc();
d->d_open = mmcsd_open;
d->d_close = mmcsd_close;
d->d_strategy = mmcsd_strategy;
d->d_ioctl = mmcsd_ioctl_disk;
@ -519,6 +553,8 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt,
d->d_stripesize = sc->erase_sector * d->d_sectorsize;
d->d_unit = cnt;
d->d_flags = DISKFLAG_CANDELETE;
if ((sc->flags & MMCSD_FLUSH_CACHE) != 0)
d->d_flags |= DISKFLAG_CANFLUSHCACHE;
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));
@ -671,6 +707,18 @@ mmcsd_detach(device_t dev)
free(part, M_DEVBUF);
}
}
if (mmcsd_flush_cache(sc) != MMC_ERR_NONE)
device_printf(dev, "failed to flush cache\n");
return (0);
}
static int
mmcsd_shutdown(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
if (mmcsd_flush_cache(sc) != MMC_ERR_NONE)
device_printf(dev, "failed to flush cache\n");
return (0);
}
@ -706,6 +754,8 @@ mmcsd_suspend(device_t dev)
MMCSD_IOCTL_UNLOCK(part);
}
}
if (mmcsd_flush_cache(sc) != MMC_ERR_NONE)
device_printf(dev, "failed to flush cache\n");
return (0);
}
@ -740,16 +790,15 @@ mmcsd_resume(device_t dev)
}
static int
mmcsd_open(struct disk *dp __unused)
{
return (0);
}
static int
mmcsd_close(struct disk *dp __unused)
mmcsd_close(struct disk *dp)
{
struct mmcsd_softc *sc;
if ((dp->d_flags & DISKFLAG_OPEN) != 0) {
sc = ((struct mmcsd_part *)dp->d_drv1)->sc;
if (mmcsd_flush_cache(sc) != MMC_ERR_NONE)
device_printf(sc->dev, "failed to flush cache\n");
}
return (0);
}
@ -943,6 +992,8 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag)
if (err != MMC_ERR_NONE)
goto switch_back;
}
if (mic->write_flag != 0)
sc->flags |= MMCSD_DIRTY;
if (mic->is_acmd != 0)
(void)mmc_wait_for_app_cmd(mmcbus, dev, rca, &cmd, 0);
else
@ -1155,6 +1206,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp)
else
cmd.opcode = MMC_READ_SINGLE_BLOCK;
} else {
sc->flags |= MMCSD_DIRTY;
if (numblocks > 1)
cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
else
@ -1340,13 +1392,18 @@ mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset,
device_t dev, mmcbus;
int err;
/* length zero is special and really means flush buffers to media */
if (!length)
return (0);
disk = arg;
part = disk->d_drv1;
sc = part->sc;
/* length zero is special and really means flush buffers to media */
if (length == 0) {
err = mmcsd_flush_cache(sc);
if (err != MMC_ERR_NONE)
return (EIO);
return (0);
}
dev = sc->dev;
mmcbus = sc->mmcbus;
@ -1396,6 +1453,14 @@ mmcsd_task(void *arg)
"mmcsd disk jobqueue", 0);
} while (bp == NULL);
MMCSD_DISK_UNLOCK(part);
if (__predict_false(bp->bio_cmd == BIO_FLUSH)) {
if (mmcsd_flush_cache(sc) != MMC_ERR_NONE) {
bp->bio_error = EIO;
bp->bio_flags |= BIO_ERROR;
}
biodone(bp);
continue;
}
if (bp->bio_cmd != BIO_READ && part->ro) {
bp->bio_error = EROFS;
bp->bio_resid = bp->bio_bcount;
@ -1453,10 +1518,35 @@ mmcsd_bus_bit_width(device_t dev)
return (8);
}
static int
mmcsd_flush_cache(struct mmcsd_softc *sc)
{
device_t dev, mmcbus;
int err;
if ((sc->flags & MMCSD_FLUSH_CACHE) == 0)
return (MMC_ERR_NONE);
dev = sc->dev;
mmcbus = sc->mmcbus;
MMCBUS_ACQUIRE_BUS(mmcbus, dev);
if ((sc->flags & MMCSD_DIRTY) == 0) {
MMCBUS_RELEASE_BUS(mmcbus, dev);
return (MMC_ERR_NONE);
}
err = mmc_switch(mmcbus, dev, sc->rca, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_FLUSH_CACHE, EXT_CSD_FLUSH_CACHE_FLUSH, 60 * 1000, true);
if (err == MMC_ERR_NONE)
sc->flags &= ~MMCSD_DIRTY;
MMCBUS_RELEASE_BUS(mmcbus, dev);
return (err);
}
static device_method_t mmcsd_methods[] = {
DEVMETHOD(device_probe, mmcsd_probe),
DEVMETHOD(device_attach, mmcsd_attach),
DEVMETHOD(device_detach, mmcsd_detach),
DEVMETHOD(device_shutdown, mmcsd_shutdown),
DEVMETHOD(device_suspend, mmcsd_suspend),
DEVMETHOD(device_resume, mmcsd_resume),
DEVMETHOD_END