diff --git a/drivers/rwbuffer.c b/drivers/rwbuffer.c index d0d4b56bd6..bd05adb4b7 100644 --- a/drivers/rwbuffer.c +++ b/drivers/rwbuffer.c @@ -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 diff --git a/include/nuttx/drivers/rwbuffer.h b/include/nuttx/drivers/rwbuffer.h index 89f09e9c6d..9515926a38 100644 --- a/include/nuttx/drivers/rwbuffer.h +++ b/include/nuttx/drivers/rwbuffer.h @@ -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 */