Author: marius
Date: Tue May 15 21:15:09 2018
New Revision: 333647
URL: https://svnweb.freebsd.org/changeset/base/333647

Log:
  - 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().

Modified:
  head/sys/dev/mmc/mmcreg.h
  head/sys/dev/mmc/mmcsd.c

Modified: head/sys/dev/mmc/mmcreg.h
==============================================================================
--- head/sys/dev/mmc/mmcreg.h   Tue May 15 21:07:11 2018        (r333646)
+++ head/sys/dev/mmc/mmcreg.h   Tue May 15 21:15:09 2018        (r333647)
@@ -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
@@ -474,6 +483,8 @@ struct mmc_request {
 #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
+
+#define        EXT_CSD_CACHE_FLUSH_POLICY_FIFO 0x01
 
 /*
  * Vendor specific EXT_CSD fields

Modified: head/sys/dev/mmc/mmcsd.c
==============================================================================
--- head/sys/dev/mmc/mmcsd.c    Tue May 15 21:07:11 2018        (r333646)
+++ head/sys/dev/mmc/mmcsd.c    Tue May 15 21:15:09 2018        (r333647)
@@ -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_off
 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_i
 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,
@@ -297,6 +307,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, con
                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, con
                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,10 +707,22 @@ 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);
+}
+
+static int
 mmcsd_suspend(device_t dev)
 {
        struct mmcsd_softc *sc = device_get_softc(dev);
@@ -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,19 +790,18 @@ mmcsd_resume(device_t dev)
 }
 
 static int
-mmcsd_open(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);
 }
 
-static int
-mmcsd_close(struct disk *dp __unused)
-{
-
-       return (0);
-}
-
 static void
 mmcsd_strategy(struct bio *bp)
 {
@@ -943,6 +992,8 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_io
                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 physi
        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
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to