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->wrnblocks = 0;
rwb->wrblockstart = (off_t)-1; rwb->wrblockstart = (off_t)-1;
rwb->wrexpectedblock = (off_t)-1;
} }
#endif #endif
@ -240,56 +239,160 @@ static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
off_t startblock, uint32_t nblocks, off_t startblock, uint32_t nblocks,
FAR const uint8_t *wrbuffer) FAR const uint8_t *wrbuffer)
{ {
int ret; uint32_t nwritten = nblocks;
/* Write writebuffer Logic */ /* Write writebuffer Logic */
rwb_wrcanceltimeout(rwb); rwb_wrcanceltimeout(rwb);
/* First: Should we flush out our cache? We would do that if (1) we already /* Is data saved in the write buffer? */
* 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
*/
if (((startblock != rwb->wrexpectedblock) && (rwb->wrnblocks)) || if (rwb->wrnblocks > 0)
((rwb->wrnblocks + nblocks) > rwb->wrmaxblocks))
{ {
finfo("writebuffer miss, expected: %08x, given: %08x\n", off_t wrbend;
rwb->wrexpectedblock, startblock); 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, wrbend = rwb->wrblockstart + rwb->wrnblocks;
rwb->wrnblocks); newend = startblock + nblocks;
if (ret < 0)
if (wrbend < startblock || rwb->wrblockstart > newend)
{ {
ferr("ERROR: Error writing multiple from cache: %d\n", -ret); /* Nothing to do */;
return ret;
} }
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->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", return nwritten;
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;
} }
#endif #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 /* We are going to invalidate a subset of the write buffer. Three
* more cases to consider: * 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) 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) else if (wrbend > startblock && wrbend <= invend)
{ {
@ -496,7 +599,7 @@ int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
ret = OK; 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) */ 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); finfo("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
/* Use the block cache unless the buffer size is bigger than block ret = nxsem_wait(&rwb->wrsem);
* cache. if (ret < 0)
*/
if (nblocks > rwb->wrmaxblocks)
{ {
/* First flush the cache */ return (ssize_t)ret;
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);
} }
else
{
/* Buffer the data in the write buffer */
ret = nxsem_wait(&rwb->wrsem); ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
if (ret < 0) rwb_semgive(&rwb->wrsem);
{
return (ssize_t)ret;
}
ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
rwb_semgive(&rwb->wrsem);
}
/* On success, return the number of blocks that we were requested to /* On success, return the number of blocks that we were requested to
* write. This is for compatibility with the normal return of a block * 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 */ uint8_t *wrbuffer; /* Allocated write buffer */
uint16_t wrnblocks; /* Number of blocks in write buffer */ uint16_t wrnblocks; /* Number of blocks in write buffer */
off_t wrblockstart; /* First block in write buffer */ off_t wrblockstart; /* First block in write buffer */
off_t wrexpectedblock; /* Next block expected */
#endif #endif
/* This is the state of the read-ahead buffering */ /* This is the state of the read-ahead buffering */