Skip to content

Commit

Permalink
readahead: common code for the implementation
Browse files Browse the repository at this point in the history
Readahead is only used when the file access has been sequential.  This
is tracked in the POSIX-like API file handles.  The FSE API does not
support readahead at this time.

Additionally, the core determines whether readahead makes sense for a
given sequential read call: see Readahead() in inodedata.c.

For simplicity, when readahead is performed, the amount requested is
always a single block of data.  It might be worthwhile to make this a
configurable value.

For context, see issue #16 in the Reliance Edge repository on GitHub.
  • Loading branch information
danielrlewis committed Jan 12, 2021
1 parent 6af1dc8 commit c8656c3
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 15 deletions.
29 changes: 29 additions & 0 deletions core/driver/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,35 @@ void RedBufferPut(
}


#if REDCONF_READAHEAD == 1
/** @brief Determine whether a block is currently buffered.
If the block has a buffer, as a side effect, that buffer is moved the tail
of the LRU list so that the buffer is more likely to persist.
@param ulBlock The block number.
@return Whether @p ulBlock is currently buffered.
*/
bool RedIsBuffered(
uint32_t ulBlock)
{
uint8_t bIdx;
bool fBuffered;

fBuffered = BufferFind(ulBlock, &bIdx);
if(fBuffered)
{
/* Promote this buffer to MRU so that it stays buffered.
*/
BufferMakeMRU(bIdx);
}

return fBuffered;
}
#endif


#if REDCONF_READ_ONLY == 0
/** @brief Flush all buffers for the active volume in the given range of blocks.
Expand Down
14 changes: 8 additions & 6 deletions core/driver/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,7 @@ REDSTATUS RedCoreFileSizeGet(
@p pulLen is populated with zero.
@param ulInode The inode number of the file to read.
@param pattern Inode access pattern (sequential, random, or unknown).
@param ullStart The file offset to read from.
@param pulLen On entry, contains the number of bytes to read; on
successful exit, contains the number of bytes actually
Expand All @@ -1518,12 +1519,13 @@ REDSTATUS RedCoreFileSizeGet(
@retval -RED_EISDIR The inode is a directory inode.
*/
REDSTATUS RedCoreFileRead(
uint32_t ulInode,
uint64_t ullStart,
uint32_t *pulLen,
void *pBuffer)
uint32_t ulInode,
ACCESSPATTERN pattern,
uint64_t ullStart,
uint32_t *pulLen,
void *pBuffer)
{
REDSTATUS ret;
REDSTATUS ret;

if(!gpRedVolume->fMounted || (pulLen == NULL))
{
Expand All @@ -1542,7 +1544,7 @@ REDSTATUS RedCoreFileRead(
ret = RedInodeMount(&ino, FTYPE_FILE, fUpdateAtime);
if(ret == 0)
{
ret = RedInodeDataRead(&ino, ullStart, pulLen, pBuffer);
ret = RedInodeDataRead(&ino, pattern, ullStart, pulLen, pBuffer);

#if (REDCONF_ATIME == 1) && (REDCONF_READ_ONLY == 0)
RedInodePut(&ino, ((ret == 0) && fUpdateAtime) ? IPUT_UPDATE_ATIME : 0U);
Expand Down
138 changes: 133 additions & 5 deletions core/driver/inodedata.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ static REDSTATUS SeekInode(CINODE *pInode, uint32_t ulBlock);
static void SeekCoord(CINODE *pInode, uint32_t ulBlock);
static REDSTATUS ReadUnaligned(CINODE *pInode, uint64_t ullStart, uint32_t ulLen, uint8_t *pbBuffer);
static REDSTATUS ReadAligned(CINODE *pInode, uint32_t ulBlockStart, uint32_t ulBlockCount, uint8_t *pbBuffer);
#if REDCONF_READAHEAD == 1
static REDSTATUS Readahead(CINODE *pInode, uint32_t ulReadLen, uint64_t ullReadEndOffset);
#endif
#if REDCONF_READ_ONLY == 0
static REDSTATUS WriteUnaligned(CINODE *pInode, uint64_t ullStart, uint32_t ulLen, const uint8_t *pbBuffer);
static REDSTATUS WriteAligned(CINODE *pInode, uint32_t ulBlockStart, uint32_t *pulBlockCount, const uint8_t *pbBuffer);
Expand All @@ -87,6 +90,7 @@ static uint32_t FreeBlockCount(void);
@param pInode A pointer to the cached inode structure of the inode from
which to read.
@param pattern Inode access pattern (sequential, random, or unknown).
@param ullStart The file offset at which to read.
@param pulLen On input, the number of bytes to attempt to read. On
successful return, populated with the number of bytes
Expand All @@ -101,12 +105,13 @@ static uint32_t FreeBlockCount(void);
@p pulLen is `NULL`; or @p pBuffer is `NULL`.
*/
REDSTATUS RedInodeDataRead(
CINODE *pInode,
uint64_t ullStart,
uint32_t *pulLen,
void *pBuffer)
CINODE *pInode,
ACCESSPATTERN pattern,
uint64_t ullStart,
uint32_t *pulLen,
void *pBuffer)
{
REDSTATUS ret = 0;
REDSTATUS ret = 0;

if(!CINODE_IS_MOUNTED(pInode) || (pulLen == NULL) || (pBuffer == NULL))
{
Expand Down Expand Up @@ -183,6 +188,15 @@ REDSTATUS RedInodeDataRead(
ret = ReadUnaligned(pInode, ullStart + ulReadIndex, ulRemaining, &pbBuffer[ulReadIndex]);
}

#if REDCONF_READAHEAD == 1
if((ret == 0) && (pattern == ACCESS_SEQUENTIAL))
{
ret = Readahead(pInode, ulLen, ullStart + ulReadIndex + ulRemaining);
}
#else
(void)pattern; /* Parameter not used. */
#endif

if(ret == 0)
{
*pulLen = ulLen;
Expand Down Expand Up @@ -1286,6 +1300,120 @@ static REDSTATUS ReadAligned(
}


#if REDCONF_READAHEAD == 1
/** @brief Initiate readahead, if doing so makes sense.
@param pInode A pointer to the cached inode structure.
@param ulReadLen Length of the just-ended read.
@param ullReadEndOffset One byte beyond where the read ended.
*/
static REDSTATUS Readahead(
CINODE *pInode,
uint32_t ulReadLen,
uint64_t ullReadEndOffset)
{
REDSTATUS ret = 0;

if(!CINODE_IS_MOUNTED(pInode))
{
REDERROR();
ret = -RED_EINVAL;
}
else
{
uint32_t ulRALogicalBlock = pInode->ulLogicalBlock + 1U;
bool fRA; /* Whether to perform readahead */

/* Assume the next read will be the same length as the just-completed
read. If it will cross over into the next logical block, then
kicking off readahead for that block makes sense.
Note ulReadLen > 0, so if the read ends on a block boundary, we will
readahead.
*/
fRA = (ullReadEndOffset >> BLOCK_SIZE_P2) != ((ullReadEndOffset + ulReadLen) >> BLOCK_SIZE_P2);

if(fRA)
{
/* Don't try to readahead beyond the end-of-file.
*/
fRA = ((uint64_t)ulRALogicalBlock << BLOCK_SIZE_P2) < pInode->pInodeBuf->ullSize;
}

/* If double indirect nodes or indirect nodes exist, ...
*/
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
if(fRA)
{
/* Compute which metadata nodes would be needed for the next block.
*/
SeekCoord(pInode, ulRALogicalBlock);

#if DINDIR_POINTERS > 0U
/* If a new double indirect node would need to be read in order to
readahead the next block, don't readahead.
*/
if((pInode->uDindirEntry != COORD_ENTRY_INVALID) && (pInode->pDindir == NULL))
{
fRA = false;
}
#endif

/* If a new indirect node would need to be read in order to
readahead the next block, don't readahead.
*/
if((pInode->uIndirEntry != COORD_ENTRY_INVALID) && (pInode->pIndir == NULL))
{
fRA = false;
}
}
#endif

if(fRA)
{
ret = SeekInode(pInode, ulRALogicalBlock);
if(ret == -RED_ENODATA)
{
/* If the data block is sparse, don't readahead.
*/
ret = 0;
fRA = false;
}
}

/* If the next read would go through the buffers...
*/
if( (ret == 0) && fRA
&& ( (ulReadLen < REDCONF_BLOCK_SIZE)
|| ((ullReadEndOffset & (REDCONF_BLOCK_SIZE - 1U)) != 0U)))
{
/* If the block is already buffered, don't readahead.
Note: If the block is buffered, as a side effect, the buffer
is moved to the tail of the LRU list, so that the buffer is
less likely to be repurposed before we get to use it.
*/
fRA = !RedIsBuffered(pInode->ulDataBlock);
}

if((ret == 0) && fRA)
{
/* Initiate the readahead.
TODO: This should probably go through the blockio.c and bdev.c
layers, rather than calling the OS service function directly.
*/
RedOsBDevReadahead(gbRedVolNum,
((uint64_t)pInode->ulDataBlock << gpRedVolume->bBlockSectorShift) + gpRedVolConf->ullSectorOffset,
1U << gpRedVolume->bBlockSectorShift);
}
}

return ret;
}
#endif /* REDCONF_READAHEAD == 1 */


#if REDCONF_READ_ONLY == 0
/** @brief Write an unaligned portion of a block.
Expand Down
5 changes: 4 additions & 1 deletion core/include/redcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ REDSTATUS RedIoFlush(uint8_t bVolNum);
void RedBufferInit(void);
REDSTATUS RedBufferGet(uint32_t ulBlock, uint16_t uFlags, void **ppBuffer);
void RedBufferPut(const void *pBuffer);
#if REDCONF_READAHEAD == 1
bool RedIsBuffered(uint32_t ulBlock);
#endif
#if REDCONF_READ_ONLY == 0
REDSTATUS RedBufferFlushRange(uint32_t ulBlockStart, uint32_t ulBlockCount);
void RedBufferDirty(const void *pBuffer);
Expand Down Expand Up @@ -220,7 +223,7 @@ REDSTATUS RedInodeIsFree(uint32_t ulInode, bool *pfFree);
REDSTATUS RedInodeBitGet(uint8_t bMR, uint32_t ulInode, uint8_t bWhich, bool *pfAllocated);
#endif

REDSTATUS RedInodeDataRead(CINODE *pInode, uint64_t ullStart, uint32_t *pulLen, void *pBuffer);
REDSTATUS RedInodeDataRead(CINODE *pInode, ACCESSPATTERN pattern, uint64_t ullStart, uint32_t *pulLen, void *pBuffer);
#if REDCONF_READ_ONLY == 0
REDSTATUS RedInodeDataWrite(CINODE *pInode, uint64_t ullStart, uint32_t *pulLen, const void *pBuffer);
#if DELETE_SUPPORTED || TRUNCATE_SUPPORTED
Expand Down
2 changes: 1 addition & 1 deletion fse/fse.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ int32_t RedFseRead(
{
uint32_t ulReadLen = ulLength;

ret = RedCoreFileRead(ulFileNum, ullFileOffset, &ulReadLen, pBuffer);
ret = RedCoreFileRead(ulFileNum, ACCESS_UNKNOWN, ullFileOffset, &ulReadLen, pBuffer);

FseLeave();

Expand Down
2 changes: 1 addition & 1 deletion include/redcoreapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ REDSTATUS RedCoreStat(uint32_t ulInode, REDSTAT *pStat);
REDSTATUS RedCoreFileSizeGet(uint32_t ulInode, uint64_t *pullSize);
#endif

REDSTATUS RedCoreFileRead(uint32_t ulInode, uint64_t ullStart, uint32_t *pulLen, void *pBuffer);
REDSTATUS RedCoreFileRead(uint32_t ulInode, ACCESSPATTERN pattern, uint64_t ullStart, uint32_t *pulLen, void *pBuffer);
#if REDCONF_READ_ONLY == 0
REDSTATUS RedCoreFileWrite(uint32_t ulInode, uint64_t ullStart, uint32_t *pulLen, const void *pBuffer);
#endif
Expand Down
10 changes: 10 additions & 0 deletions include/redmisc.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,15 @@ typedef enum
} FTYPE;


/** @brief Inode access pattern.
*/
typedef enum
{
ACCESS_UNKNOWN, /**< Unknown access pattern (not being tracked) */
ACCESS_SEQUENTIAL, /**< Sequential access pattern. */
ACCESS_RANDOM /**< Random access pattern. Anything non-sequential is "random". */
} ACCESSPATTERN;


#endif

20 changes: 19 additions & 1 deletion posix/posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@
#define HFLAG_READABLE 0x02U /* Handle is readable. */
#define HFLAG_WRITEABLE 0x04U /* Handle is writeable. */
#define HFLAG_APPENDING 0x08U /* Handle was opened in append mode. */
#if REDCONF_READAHEAD == 1
#define HFLAG_RANDOM 0x10U /* Handle has been accessed non-sequentially. */

#define HANDLE_ACCESS(handle) ((((handle)->bFlags) & HFLAG_RANDOM) ? ACCESS_RANDOM : ACCESS_SEQUENTIAL)
#else
#define HANDLE_ACCESS(handle) ACCESS_UNKNOWN
#endif

/* @brief Handle structure, used to implement file descriptors and directory
streams.
Expand Down Expand Up @@ -1605,7 +1612,7 @@ int32_t red_read(
if(ret == 0)
{
ulLenRead = ulLength;
ret = RedCoreFileRead(pHandle->ulInode, pHandle->o.ullFileOffset, &ulLenRead, pBuffer);
ret = RedCoreFileRead(pHandle->ulInode, HANDLE_ACCESS(pHandle), pHandle->o.ullFileOffset, &ulLenRead, pBuffer);
}

if(ret == 0)
Expand Down Expand Up @@ -1959,6 +1966,17 @@ int64_t red_lseek(
}
else
{
#if REDCONF_READAHEAD == 1
/* An explicit seek changes the access pattern to random,
unless the seek is a no-op or the seek is returning to
the start of the file.
*/
if((llNewOffset != 0) && (pHandle->o.ullFileOffset != (uint64_t)llNewOffset))
{
pHandle->bFlags |= HFLAG_RANDOM;
}
#endif

pHandle->o.ullFileOffset = (uint64_t)llNewOffset;
llReturn = llNewOffset;
}
Expand Down

0 comments on commit c8656c3

Please sign in to comment.