rwbuffer: Optimize the buffer algorithm

avoid the buffer flush as much as possible

Change-Id: I902f374e9540b36bd0b0c77a34cab5014a2c24fc
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
Xiang Xiao 2020-07-14 17:22:02 +08:00 committed by Brennan Ashton
parent e7034c102f
commit aa57174eb9
2 changed files with 142 additions and 66 deletions

View File

@ -136,7 +136,6 @@ static inline void rwb_resetwrbuffer(FAR struct rwbuffer_s *rwb)
rwb->wrnblocks = 0;
rwb->wrblockstart = (off_t)-1;
rwb->wrexpectedblock = (off_t)-1;
}
#endif
@ -240,56 +239,160 @@ static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
off_t startblock, uint32_t nblocks,
FAR const uint8_t *wrbuffer)
{
int ret;
uint32_t nwritten = nblocks;
/* Write writebuffer Logic */
rwb_wrcanceltimeout(rwb);
/* First: Should we flush out our cache? We would do that if (1) we already
* buffering blocks and the next block writing is not in the same sequence,
* or (2) the number of blocks would exceed our allocated buffer capacity
*/
/* Is data saved in the write buffer? */
if (((startblock != rwb->wrexpectedblock) && (rwb->wrnblocks)) ||
((rwb->wrnblocks + nblocks) > rwb->wrmaxblocks))
if (rwb->wrnblocks > 0)
{
finfo("writebuffer miss, expected: %08x, given: %08x\n",
rwb->wrexpectedblock, startblock);
off_t wrbend;
off_t newend;
/* Flush the write buffer */
/* Now there are five cases:
*
* 1. We update the non-overlapping region
*/
ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart,
rwb->wrnblocks);
if (ret < 0)
wrbend = rwb->wrblockstart + rwb->wrnblocks;
newend = startblock + nblocks;
if (wrbend < startblock || rwb->wrblockstart > newend)
{
ferr("ERROR: Error writing multiple from cache: %d\n", -ret);
return ret;
/* Nothing to do */;
}
rwb_resetwrbuffer(rwb);
/* 2. We update the entire write buffer. */
else if (rwb->wrblockstart > startblock && wrbend < newend)
{
rwb->wrnblocks = 0;
}
/* We are going to update a subset of the write buffer. Three
* more cases to consider:
*
* 3. We update a portion in the middle of the write buffer
*/
else if (rwb->wrblockstart <= startblock && wrbend >= newend)
{
FAR uint8_t *dest;
size_t offset;
/* Copy the data to the middle of write buffer */
offset = startblock - rwb->wrblockstart;
dest = rwb->wrbuffer + offset * rwb->blocksize;
memcpy(dest, wrbuffer, nblocks * rwb->blocksize);
nblocks = 0;
}
/* 4. We upate a portion at the end of the write buffer */
else if (wrbend >= startblock && wrbend <= newend)
{
FAR uint8_t *dest;
size_t offset;
size_t ncopy;
/* Copy the data from the updating region to the end
* of the write buffer.
*/
offset = rwb->wrnblocks - (wrbend - startblock);
ncopy = rwb->wrmaxblocks - offset;
if (ncopy > nblocks)
{
ncopy = nblocks;
}
dest = rwb->wrbuffer + offset * rwb->blocksize;
memcpy(dest, wrbuffer, ncopy * rwb->blocksize);
rwb->wrnblocks = offset + ncopy;
wrbuffer += ncopy * rwb->blocksize;
startblock += ncopy;
nblocks -= ncopy;
}
/* 5. We update a portion at the beginning of the write buffer */
else /* if (rwb->wrblockstart >= startblock && wrbend >= newend) */
{
FAR uint8_t *dest;
FAR const uint8_t *src;
size_t ncopy;
DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend >= newend);
/* Move the cached data to the end of the write buffer */
ncopy = rwb->wrblockstart - startblock;
if (ncopy > rwb->wrmaxblocks - rwb->wrnblocks)
{
ncopy = rwb->wrmaxblocks - rwb->wrnblocks;
}
dest = rwb->wrbuffer + ncopy * rwb->blocksize;
memmove(dest, rwb->wrbuffer, ncopy * rwb->blocksize);
rwb->wrblockstart -= ncopy;
rwb->wrnblocks += ncopy;
/* Copy the data from the updating region to the beginning
* of the write buffer.
*/
ncopy = newend - rwb->wrblockstart;
src = wrbuffer + (nblocks - ncopy) * rwb->blocksize;
memcpy(rwb->wrbuffer, src, ncopy * rwb->blocksize);
nblocks -= ncopy;
}
}
/* writebuffer is empty? Then initialize it */
/* Use the block cache unless the buffer size is bigger than block cache */
if (rwb->wrnblocks == 0)
if (nblocks > rwb->wrmaxblocks)
{
finfo("Fresh cache starting at block: 0x%08x\n", startblock);
ssize_t ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
if (ret < 0)
{
return ret;
}
}
else if (nblocks)
{
/* Flush the write buffer */
if (rwb->wrnblocks > 0)
{
ssize_t ret = rwb->wrflush(rwb->dev, rwb->wrbuffer,
rwb->wrblockstart, rwb->wrnblocks);
if (ret < 0)
{
return ret;
}
}
/* Buffer the data in the write buffer */
memcpy(rwb->wrbuffer, wrbuffer, nblocks * rwb->blocksize);
rwb->wrblockstart = startblock;
rwb->wrnblocks = nblocks;
}
/* Add data to cache */
if (rwb->wrnblocks > 0)
{
rwb_wrstarttimeout(rwb);
}
finfo("writebuffer: copying %d bytes from %p to %p\n",
nblocks * rwb->blocksize, wrbuffer,
&rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize]);
memcpy(&rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize],
wrbuffer, nblocks * rwb->blocksize);
rwb->wrnblocks += nblocks;
rwb->wrexpectedblock = rwb->wrblockstart + rwb->wrnblocks;
rwb_wrstarttimeout(rwb);
return nblocks;
return nwritten;
}
#endif
@ -454,7 +557,7 @@ int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
/* We are going to invalidate a subset of the write buffer. Three
* more cases to consider:
*
* 2. We invalidate a portion in the middle of the write buffer
* 3. We invalidate a portion in the middle of the write buffer
*/
else if (rwb->wrblockstart < startblock && wrbend > invend)
@ -488,7 +591,7 @@ int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
}
}
/* 3. We invalidate a portion at the end of the write buffer */
/* 4. We invalidate a portion at the end of the write buffer */
else if (wrbend > startblock && wrbend <= invend)
{
@ -496,7 +599,7 @@ int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
ret = OK;
}
/* 4. We invalidate a portion at the beginning of the write buffer */
/* 5. We invalidate a portion at the beginning of the write buffer */
else /* if (rwb->wrblockstart >= startblock && wrbend > invend) */
{
@ -1005,40 +1108,14 @@ ssize_t rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
{
finfo("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
/* Use the block cache unless the buffer size is bigger than block
* cache.
*/
if (nblocks > rwb->wrmaxblocks)
ret = nxsem_wait(&rwb->wrsem);
if (ret < 0)
{
/* First flush the cache */
ret = nxsem_wait(&rwb->wrsem);
if (ret < 0)
{
return (ssize_t)ret;
}
rwb_wrflush(rwb);
rwb_semgive(&rwb->wrsem);
/* Then transfer the data directly to the media */
ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
return (ssize_t)ret;
}
else
{
/* Buffer the data in the write buffer */
ret = nxsem_wait(&rwb->wrsem);
if (ret < 0)
{
return (ssize_t)ret;
}
ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
rwb_semgive(&rwb->wrsem);
}
ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
rwb_semgive(&rwb->wrsem);
/* On success, return the number of blocks that we were requested to
* write. This is for compatibility with the normal return of a block

View File

@ -144,7 +144,6 @@ struct rwbuffer_s
uint8_t *wrbuffer; /* Allocated write buffer */
uint16_t wrnblocks; /* Number of blocks in write buffer */
off_t wrblockstart; /* First block in write buffer */
off_t wrexpectedblock; /* Next block expected */
#endif
/* This is the state of the read-ahead buffering */