This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 5a45130d5c usbdev/usbmsc: introduce USBMSC_WRMULTIPLE for faster writes
5a45130d5c is described below

commit 5a45130d5c40a28388d997b6643b62690803be61
Author: Eero Nurkkala <eero.nurkk...@offcode.fi>
AuthorDate: Tue Jul 12 08:44:55 2022 +0300

    usbdev/usbmsc: introduce USBMSC_WRMULTIPLE for faster writes
    
    This patch introduces a configuration option USBMSC_WRMULTIPLE,
    which is used to store multiple blocks into a larger chunk that
    then gets written via the mmcsd_writemultiple() (in case mmcsd
    is used).
    
    The bottleneck with the current implementation is the poor
    performance due to short block writes.  USBMSC_DRVR_WRITE()
    always writes only one sector (with eMMC that's usually 512 bytes).
    eMMC devices usually erase much larger regions with near constant
    time (see Jedec JESD84-B51, Extended CSD register byte [225],
    SUPER_PAGE_SIZE): 'This register defines one or multiple of
    programmable boundary unit that is programmed at the same time.'
    
    If USBMSC_WRMULTIPLE is defined, then USBMSC_NWRREQS is used to
    allocate the write buffer size.  We don't want this to be the
    default behavior yet as this may reveal unseen bugs in usb drivers
    due to the faster overall performance.
    
    Sample configurations with measured performance:
    
      - Without USBMSC_WRMULTIPLE: 470 Kb/s
      - With USBMSC_WRMULTIPLE, CONFIG_USBMSC_NWRREQS=4: 1.1 Mb/s
        (dd with bs=2k)
      - With USBMSC_WRMULTIPLE, CONFIG_USBMSC_NWRREQS=16: 5.2 Mb/s
        (dd with bs=8k)
    
    No doubt, this feature alone may make the mass storage work 10
    times faster than before with eMMC cards.
    
    Signed-off-by: Eero Nurkkala <eero.nurkk...@offcode.fi>
---
 drivers/usbdev/Kconfig       | 14 ++++++++++++++
 drivers/usbdev/usbmsc.c      |  9 +++++++++
 drivers/usbdev/usbmsc_scsi.c | 46 ++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/drivers/usbdev/Kconfig b/drivers/usbdev/Kconfig
index b218515c79..1d7a418f51 100644
--- a/drivers/usbdev/Kconfig
+++ b/drivers/usbdev/Kconfig
@@ -799,6 +799,20 @@ config USBMSC_NRDREQS
        ---help---
                The number of write/read requests that can be in flight
 
+config USBMSC_WRMULTIPLE
+       bool "Write multiple blocks at once if possible"
+       default n
+       ---help---
+               Store multiple blocks and write them in a single request.  This
+               speeds up the writing significantly with eMMC devices, for 
example,
+               because writing 512 bytes may be as fast as writing the complete
+               SUPER_PAGE_SIZE (see extended CSD [225] bits 0-3), which may be 
up
+               to 64Kb.  Real-life example with different block sizes, using 
the
+               dd command with argument bs=512, bs=8k (USBMSC_NWRREQS = 4, and 
16):
+               512b: 470 kb/s, 8k: 5.3 Mb/s.  This is more than a tenfold 
increase
+               in the throughput.  Without this option enabled, the block 
driver's
+               block size is always used, which is usually 512 bytes.
+
 config USBMSC_BULKINREQLEN
        int "Bulk IN request size"
        default 512 if USBDEV_DUALSPEED
diff --git a/drivers/usbdev/usbmsc.c b/drivers/usbdev/usbmsc.c
index cd7a683e8a..5d4a9cca0f 100644
--- a/drivers/usbdev/usbmsc.c
+++ b/drivers/usbdev/usbmsc.c
@@ -1514,7 +1514,12 @@ int usbmsc_bindlun(FAR void *handle, FAR const char 
*drvrpath,
 
   if (!priv->iobuffer)
     {
+#ifdef CONFIG_USBMSC_WRMULTIPLE
+      priv->iobuffer = (FAR uint8_t *)kmm_malloc(geo.geo_sectorsize *
+                                                 CONFIG_USBMSC_NWRREQS);
+#else
       priv->iobuffer = (FAR uint8_t *)kmm_malloc(geo.geo_sectorsize);
+#endif
       if (!priv->iobuffer)
         {
           usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_ALLOCIOBUFFER),
@@ -1522,7 +1527,11 @@ int usbmsc_bindlun(FAR void *handle, FAR const char 
*drvrpath,
           return -ENOMEM;
         }
 
+#ifdef CONFIG_USBMSC_WRMULTIPLE
+      priv->iosize = geo.geo_sectorsize * CONFIG_USBMSC_NWRREQS;
+#else
       priv->iosize = geo.geo_sectorsize;
+#endif
     }
   else if (priv->iosize < geo.geo_sectorsize)
     {
diff --git a/drivers/usbdev/usbmsc_scsi.c b/drivers/usbdev/usbmsc_scsi.c
index 5038cab2c9..b0bde3d7d8 100644
--- a/drivers/usbdev/usbmsc_scsi.c
+++ b/drivers/usbdev/usbmsc_scsi.c
@@ -2444,8 +2444,24 @@ static int usbmsc_cmdwritestate(FAR struct usbmsc_dev_s 
*priv)
           src  = &req->buf[xfrd - priv->nreqbytes];
           dest = &priv->iobuffer[priv->nsectbytes];
 
-          nbytes = MIN(lun->sectorsize - priv->nsectbytes, priv->nreqbytes);
+#ifdef CONFIG_USBMSC_WRMULTIPLE
+          /* nbytes may end up being zero, after which the loop no longer
+           * proceeds but will be stuck forever.  Make sure nbytes isn't
+           * zero.
+           */
 
+          if (lun->sectorsize > priv->nsectbytes)
+            {
+              nbytes = MIN(lun->sectorsize - priv->nsectbytes,
+                           priv->nreqbytes);
+            }
+          else
+            {
+              nbytes = priv->nreqbytes;
+            }
+#else
+          nbytes = MIN(lun->sectorsize - priv->nsectbytes, priv->nreqbytes);
+#endif
           /* Copy the data from the sector buffer to the USB request and
            * update counts
            */
@@ -2454,9 +2470,34 @@ static int usbmsc_cmdwritestate(FAR struct usbmsc_dev_s 
*priv)
           priv->nsectbytes += nbytes;
           priv->nreqbytes  -= nbytes;
 
+#ifdef CONFIG_USBMSC_WRMULTIPLE
+          uint32_t nrbufs = MIN(priv->u.xfrlen, CONFIG_USBMSC_NWRREQS);
+
           /* Is the I/O buffer full? */
 
-          if (priv->nsectbytes >= lun->sectorsize)
+          if ((priv->nsectbytes >= lun->sectorsize * priv->u.xfrlen) ||
+              (priv->nsectbytes >= lun->sectorsize * CONFIG_USBMSC_NWRREQS))
+            {
+              /* Yes.. Write next sectors */
+
+              nwritten = USBMSC_DRVR_WRITE(lun, priv->iobuffer,
+                                           priv->sector, nrbufs);
+              if (nwritten < 0)
+                {
+                  usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDWRITEWRITEFAIL),
+                           -nwritten);
+                  lun->sd     = SCSI_KCQME_WRITEFAULTAUTOREALLOCFAILED;
+                  lun->sdinfo = priv->sector;
+                  goto errout;
+                }
+
+              priv->nsectbytes = 0;
+              priv->residue   -= lun->sectorsize * nrbufs;
+              priv->u.xfrlen  -= nrbufs;
+              priv->sector    += nrbufs;
+            }
+#else
+          if ((priv->nsectbytes >= lun->sectorsize))
             {
               /* Yes.. Write the next sector */
 
@@ -2476,6 +2517,7 @@ static int usbmsc_cmdwritestate(FAR struct usbmsc_dev_s 
*priv)
               priv->u.xfrlen--;
               priv->sector++;
             }
+#endif
         }
 
       /* In either case, we are finished with this read request and can

Reply via email to