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