int rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock, size_t nblocks, FAR const uint8_t *wrbuffer) { int ret = OK; #ifdef CONFIG_DRVR_READAHEAD if (rwb->rhmaxblocks > 0) { /* If the new write data overlaps any part of the read buffer, then * flush the data from the read buffer. We could attempt some more * exotic handling -- but this simple logic is well-suited for simple * streaming applications. */ rwb_semtake(&rwb->rhsem); if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks)) { rwb_resetrhbuffer(rwb); } rwb_semgive(&rwb->rhsem); } #endif #ifdef CONFIG_DRVR_WRITEBUFFER if (rwb->wrmaxblocks > 0) { fvdbg("startblock=%d wrbuffer=%p\n", startblock, wrbuffer); /* Use the block cache unless the buffer size is bigger than block cache */ if (nblocks > rwb->wrmaxblocks) { /* First flush the cache */ rwb_semtake(&rwb->wrsem); 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 = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer); } /* On success, return the number of blocks that we were requested to * write. This is for compatibility with the normal return of a block * driver write method */ } else #endif { /* No write buffer.. just pass the write operation through via the * flush callback. */ ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks); } return ret; }
int rwb_mediaremoved(FAR struct rwbuffer_s *rwb) { #ifdef CONFIG_FS_WRITEBUFFER rwb_semtake(&rwb->wrsem); rwb_resetwrbuffer(rwb); rwb_semgive(&rwb->wrsem); #endif #ifdef CONFIG_FS_READAHEAD rwb_semtake(&rwb->rhsem); rwb_resetrhbuffer(rwb); rwb_semgive(&rwb->rhsem); #endif return 0; }
static void rwb_wrflush(struct rwbuffer_s *rwb) { int ret; fvdbg("Timeout!\n"); rwb_semtake(&rwb->wrsem); if (rwb->wrnblocks > 0) { fvdbg("Flushing: blockstart=0x%08lx nblocks=%d from buffer=%p\n", (long)rwb->wrblockstart, rwb->wrnblocks, rwb->wrbuffer); /* Flush cache. On success, the flush method will return the number * of blocks written. Anything other than the number requested is * an error. */ ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks); if (ret != rwb->wrnblocks) { fdbg("ERROR: Error flushing write buffer: %d\n", ret); } rwb_resetwrbuffer(rwb); } rwb_semgive(&rwb->wrsem); }
int rwb_mediaremoved(FAR struct rwbuffer_s *rwb) { #ifdef CONFIG_DRVR_WRITEBUFFER if (rwb->wrmaxblocks > 0) { rwb_semtake(&rwb->wrsem); rwb_resetwrbuffer(rwb); rwb_semgive(&rwb->wrsem); } #endif #ifdef CONFIG_DRVR_READAHEAD if (rwb->rhmaxblocks > 0) { rwb_semtake(&rwb->rhsem); rwb_resetrhbuffer(rwb); rwb_semgive(&rwb->rhsem); } #endif return OK; }
static void rwb_wrtimeout(FAR void *arg) { /* The following assumes that the size of a pointer is 4-bytes or less */ FAR struct rwbuffer_s *rwb = (struct rwbuffer_s *)arg; DEBUGASSERT(rwb != NULL); /* If a timeout elapses with with write buffer activity, this watchdog * handler function will be evoked on the thread of execution of the * worker thread. */ rwb_semtake(&rwb->wrsem); rwb_wrflush(rwb); rwb_semgive(&rwb->wrsem); }
int rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock, uint32_t nblocks, FAR uint8_t *rdbuffer) { uint32_t remaining; fvdbg("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 if (rhmaxblocks > 0) { /* Loop until we have read all of the requested blocks */ rwb_semtake(&rwb->rhsem); for (remaining = nblocks; remaining > 0;) { /* Is there anything in the read-ahead buffer? */ if (rwb->rhnblocks > 0) { off_t startblock = startblock; size_t nbufblocks = 0; off_t bufferend; /* Loop for each block we find in the read-head buffer. Count * the number of buffers that we can read from read-ahead * buffer. */ bufferend = rwb->rhblockstart + rwb->rhnblocks; while ((startblock >= rwb->rhblockstart) && (startblock < bufferend) && (remaining > 0)) { /* This is one more that we will read from the read ahead * buffer. */ nbufblocks++; /* And one less that we will read from the media */ startblock++; remaining--; } /* Then read the data from the read-ahead buffer */ rwb_bufferread(rwb, startblock, nbufblocks, &rdbuffer); } /* If we did not get all of the data from the buffer, then we have * to refill the buffer and try again. */ if (remaining > 0) { int ret = rwb_rhreload(rwb, startblock); if (ret < 0) { fdbg("ERROR: Failed to fill the read-ahead buffer: %d\n", ret); return ret; } } } /* On success, return the number of blocks that we were requested to * read. This is for compatibility with the normal return of a block * driver read method */ rwb_semgive(&rwb->rhsem); ret = nblocks; } else #else { /* No read-ahead buffering, (re)load the data directly into * the user buffer. */ ret = rwb->rhreload(rwb->dev, startblock, nblocks, rdbuffer); } #endif return ret; }
int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb, off_t startblock, size_t blockcount) { int ret; if (rwb->rhmaxblocks > 0 && rhnblocks > 0) { off_t rhbend; off_t invend; fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount); rwb_semtake(&rwb->rhsem); /* Now there are five cases: * * 1. We invalidate nothing */ rhbend = rwb->rhblockstart + rwb->rhnblocks; invend = startblock + blockcount; if (rwb->rhblockstart > invend || rhbend < startblock) { ret = OK; } /* 2. We invalidate the entire read-ahead buffer. */ else if (rwb->rhblockstart >= startblock && rhbend <= invend) { rwb->rhnblocks = 0; ret = OK; } /* We are going to invalidate a subset of the read-ahead buffer. * Three more cases to consider: * * 2. We invalidate a portion in the middle of the write buffer */ else if (rwb->rhblockstart < startblock && rhbend > invend) { /* Keep the blocks at the beginning of the buffer up the * start of the invalidated region. */ rwb->rhnblocks = startblock - rwb->rhblockstart; ret = OK; } /* 3. We invalidate a portion at the end of the read-ahead buffer */ else if (rhbend > startblock && rhbend <= invend) { rwb->rhnblocks = rhbend - startblock; ret = OK; } /* 4. We invalidate a portion at the beginning of the write buffer */ else /* if (rwb->rhblockstart >= startblock && rhbend < invend) */ { /* Let's just force the whole read-ahead buffer to be reloaded. * That might cost s small amount of performance, but well worth * the lower complexity. */ DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend < invend); rwb->rhnblocks = 0; ret = OK; } rwb_semgive(&rwb->rhsem); } return ret; }
int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb, off_t startblock, size_t blockcount) { int ret; if (rwb->wrmaxblocks > 0 && wrnblocks > 0) { off_t wrbend; off_t invend; fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount); rwb_semtake(&rwb->wrsem); /* Now there are five cases: * * 1. We invalidate nothing */ wrbend = rwb->wrblockstart + rwb->wrnblocks; invend = startblock + blockcount; if (rwb->wrblockstart > invend || wrbend < startblock) { ret = OK; } /* 2. We invalidate the entire write buffer. */ else if (rwb->wrblockstart >= startblock && wrbend <= invend) { rwb->wrnblocks = 0; ret = OK; } /* 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 */ else if (rwb->wrblockstart < startblock && wrbend > invend) { uint8_t *src; off_t block; off_t offset; size_t nblocks; /* Write the blocks at the end of the media to hardware */ nblocks = wrbend - invend; block = invend; offset = block - rwb->wrblockstart; src = rwb->wrbuffer + offset * rwb->blocksize; ret = rwb->wrflush(rwb->dev, block, nblocks, src); if (ret < 0) { fdbg("ERROR: wrflush failed: %d\n", ret); } /* Keep the blocks at the beginning of the buffer up the * start of the invalidated region. */ else { rwb->wrnblocks = startblock - rwb->wrblockstart; ret = OK; } } /* 3. We invalidate a portion at the end of the write buffer */ else if (wrbend > startblock && wrbend <= invend) { rwb->wrnblocks = wrbend - startblock; ret = OK; } /* 4. We invalidate a portion at the beginning of the write buffer */ else /* if (rwb->wrblockstart >= startblock && wrbend < invend) */ { uint8_t *src; size_t ninval; size_t nkeep; DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend < invend); /* Copy the data from the uninvalidated region to the beginning * of the write buffer. * * First calculate the source and destination of the transfer. */ ninval = invend - rwb->wrblockstart; src = rwb->wrbuffer + ninval * rwb->blocksize; /* Calculate the number of blocks we are keeping. We keep * the ones that we don't invalidate. */ nkeep = rwb->wrnblocks - ninval; /* Then move the data that we are keeping to the beginning * the write buffer. */ memcpy(rwb->wrbuffer, 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->wrblockstart = invend; rwb->wrnblocks = nkeep; ret = OK; } rwb_semgive(&rwb->wrsem); } return ret; }