drivers/rwbuffer.c: Improve the rwb_read/write overlap performance

This commit is contained in:
zhuyanlin
2018-11-09 08:10:25 -06:00
committed by Gregory Nutt
parent 44c9728a60
commit 0849af133e
+120 -39
View File
@@ -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);