mirror of
https://github.com/apache/nuttx.git
synced 2026-06-02 01:21:26 +08:00
drivers/rwbuffer.c: Improve the rwb_read/write overlap performance
This commit is contained in:
+120
-39
@@ -620,14 +620,39 @@ int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb,
|
|||||||
|
|
||||||
else /* if (rwb->rhblockstart >= startblock && rhbend > invend) */
|
else /* if (rwb->rhblockstart >= startblock && rhbend > invend) */
|
||||||
{
|
{
|
||||||
/* Let's just force the whole read-ahead buffer to be reloaded.
|
uint8_t *src;
|
||||||
* That might cost s small amount of performance, but well worth
|
size_t ninval;
|
||||||
* the lower complexity.
|
size_t nkeep;
|
||||||
*/
|
|
||||||
|
|
||||||
DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend > invend);
|
DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend > invend);
|
||||||
rwb->rhnblocks = 0;
|
/* Copy the data from the uninvalidated region to the beginning
|
||||||
ret = OK;
|
* of the read buffer.
|
||||||
|
*
|
||||||
|
* First calculate the source and destination of the transfer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ninval = invend - rwb->rhblockstart;
|
||||||
|
src = rwb->rhbuffer + ninval * rwb->blocksize;
|
||||||
|
|
||||||
|
/* Calculate the number of blocks we are keeping. We keep
|
||||||
|
* the ones that we don't invalidate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
nkeep = rwb->rhnblocks - ninval;
|
||||||
|
|
||||||
|
/* Then move the data that we are keeping to the beginning
|
||||||
|
* the read buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
memmove(rwb->rhbuffer, src, nkeep * rwb->blocksize);
|
||||||
|
|
||||||
|
/* Update the block info. The first block is now the one just
|
||||||
|
* after the invalidation region and the number buffered blocks
|
||||||
|
* is the number that we kept.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rwb->rhblockstart = invend;
|
||||||
|
rwb->rhnblocks = nkeep;
|
||||||
}
|
}
|
||||||
|
|
||||||
rwb_semgive(&rwb->rhsem);
|
rwb_semgive(&rwb->rhsem);
|
||||||
@@ -762,46 +787,19 @@ void rwb_uninitialize(FAR struct rwbuffer_s *rwb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: rwb_read
|
* Name: rwb_read_
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
|
static ssize_t rwb_read_(FAR struct rwbuffer_s *rwb, off_t startblock,
|
||||||
size_t nblocks, FAR uint8_t *rdbuffer)
|
size_t nblocks, FAR uint8_t *rdbuffer)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_DRVR_READAHEAD
|
|
||||||
size_t remaining;
|
|
||||||
#endif
|
|
||||||
int ret = OK;
|
int ret = OK;
|
||||||
|
|
||||||
finfo("startblock=%ld nblocks=%ld rdbuffer=%p\n",
|
|
||||||
(long)startblock, (long)nblocks, rdbuffer);
|
|
||||||
|
|
||||||
#ifdef CONFIG_DRVR_WRITEBUFFER
|
|
||||||
/* If the new read data overlaps any part of the write buffer, then
|
|
||||||
* flush the write data onto the physical media before reading. We
|
|
||||||
* could attempt some more exotic handling -- but this simple logic
|
|
||||||
* is well-suited for simple streaming applications.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (rwb->wrmaxblocks > 0)
|
|
||||||
{
|
|
||||||
/* If the write buffer overlaps the block(s) requested, then flush the
|
|
||||||
* write buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
rwb_semtake(&rwb->wrsem);
|
|
||||||
if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks))
|
|
||||||
{
|
|
||||||
rwb_wrflush(rwb);
|
|
||||||
}
|
|
||||||
|
|
||||||
rwb_semgive(&rwb->wrsem);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_DRVR_READAHEAD
|
#ifdef CONFIG_DRVR_READAHEAD
|
||||||
if (rwb->rhmaxblocks > 0)
|
if (rwb->rhmaxblocks > 0)
|
||||||
{
|
{
|
||||||
|
size_t remaining;
|
||||||
|
|
||||||
/* Loop until we have read all of the requested blocks */
|
/* Loop until we have read all of the requested blocks */
|
||||||
|
|
||||||
rwb_semtake(&rwb->rhsem);
|
rwb_semtake(&rwb->rhsem);
|
||||||
@@ -857,7 +855,7 @@ ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
|
|||||||
ret = nblocks;
|
ret = nblocks;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#else
|
#endif
|
||||||
{
|
{
|
||||||
/* No read-ahead buffering, (re)load the data directly into
|
/* No read-ahead buffering, (re)load the data directly into
|
||||||
* the user buffer.
|
* the user buffer.
|
||||||
@@ -865,11 +863,82 @@ ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
|
|||||||
|
|
||||||
ret = rwb->rhreload(rwb->dev, rdbuffer, startblock, nblocks);
|
ret = rwb->rhreload(rwb->dev, rdbuffer, startblock, nblocks);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return (ssize_t)ret;
|
return (ssize_t)ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: rwb_read
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
|
||||||
|
size_t nblocks, FAR uint8_t *rdbuffer)
|
||||||
|
{
|
||||||
|
int ret = OK;
|
||||||
|
size_t readblocks = 0;
|
||||||
|
|
||||||
|
finfo("startblock=%ld nblocks=%ld rdbuffer=%p\n",
|
||||||
|
(long)startblock, (long)nblocks, rdbuffer);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DRVR_WRITEBUFFER
|
||||||
|
/* If the new read data overlaps any part of the write buffer, we
|
||||||
|
* directly copy write buffer to read buffer. This boost performance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (rwb->wrmaxblocks > 0)
|
||||||
|
{
|
||||||
|
/* If the write buffer overlaps the block(s) requested */
|
||||||
|
|
||||||
|
rwb_semtake(&rwb->wrsem);
|
||||||
|
if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks))
|
||||||
|
{
|
||||||
|
size_t rdblocks = 0;
|
||||||
|
size_t wrnpass = 0;
|
||||||
|
|
||||||
|
if (rwb->wrblockstart > startblock)
|
||||||
|
{
|
||||||
|
rdblocks = rwb->wrblockstart - startblock;
|
||||||
|
ret = rwb_read_(rwb, startblock, rdblocks, rdbuffer);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
rwb_semgive(&rwb->wrsem);
|
||||||
|
return (ssize_t)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
startblock += ret;
|
||||||
|
nblocks -= ret;
|
||||||
|
rdbuffer += ret * rwb->blocksize;
|
||||||
|
readblocks += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rwb->wrblockstart < startblock)
|
||||||
|
{
|
||||||
|
wrnpass = startblock - rwb->wrblockstart;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdblocks = nblocks > (rwb->wrnblocks - wrnpass) ?
|
||||||
|
(rwb->wrnblocks - wrnpass) : nblocks;
|
||||||
|
memcpy(rdbuffer, &rwb->wrbuffer[wrnpass * rwb->blocksize],
|
||||||
|
rdblocks * rwb->blocksize);
|
||||||
|
|
||||||
|
startblock += rdblocks;
|
||||||
|
nblocks -= rdblocks;
|
||||||
|
rdbuffer += rdblocks * rwb->blocksize;
|
||||||
|
readblocks += rdblocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
rwb_semgive(&rwb->wrsem);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = rwb_read_(rwb, startblock, nblocks, rdbuffer);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return readblocks + ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: rwb_write
|
* Name: rwb_write
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -891,7 +960,19 @@ ssize_t rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
|
|||||||
rwb_semtake(&rwb->rhsem);
|
rwb_semtake(&rwb->rhsem);
|
||||||
if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
|
if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_DRVR_INVALIDATE
|
||||||
|
/* Just invalidate the read buffer startblock + nblocks data */
|
||||||
|
|
||||||
|
ret = rwb_invalidate_readahead(rwb, startblock, nblocks);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ferr("ERROR: rwb_invalidate_readahead failed: %d\n", ret);
|
||||||
|
rwb_semgive(&rwb->rhsem);
|
||||||
|
return (ssize_t)ret;
|
||||||
|
}
|
||||||
|
#else
|
||||||
rwb_resetrhbuffer(rwb);
|
rwb_resetrhbuffer(rwb);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
rwb_semgive(&rwb->rhsem);
|
rwb_semgive(&rwb->rhsem);
|
||||||
|
|||||||
Reference in New Issue
Block a user