twl_wrapsdk/build/libraries/fatfs/ARM7/prblock.c
Shirait 4fe5688d29 add SD driver and FATFS library (tentative)
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370
2007-05-30 10:22:28 +00:00

1189 lines
42 KiB
C

/*
* EBS - RTFS (Real Time File Manager)
*
* Copyright EBS Inc. 1987-2003
* All rights reserved.
* This code may not be redistributed in source or linkable object form
* without the consent of its author.
*/
/* PRBLOCK.C - ERTFS-PRO Directory block buffering routines */
#include <rtfs.h>
static void pc_add_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk);
static void pc_release_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk);
static BLKBUFF *pc_allocate_blk(DDRIVE *pdrive, BLKBUFFCNTXT *pbuffcntxt);
/* Debugging tools to be removed in he final product */
#define DEBUG_BLOCK_CODE 0
#define DEBUG_FAT_CODE 0
void debug_check_blocks(BLKBUFFCNTXT *pbuffcntxt, int numblocks, char *where);
void debug_check_fat(FATBUFFCNTXT *pfatbuffcntxt, char *where);
void debug_break(char *where, char *message);
#if (DEBUG_BLOCK_CODE)
#define DEBUG_CHECK_BLOCKS(X,Y,Z) debug_check_blocks(X,Y,X);
#else
#define DEBUG_CHECK_BLOCKS(X,Y,Z)
#endif
#if (DEBUG_FAT_CODE)
#define DEBUG_CHECK_FAT(X,Y) debug_check_fat(X,Y);
#else
#define DEBUG_CHECK_FAT(X,Y)
#endif
#if (!INCLUDE_FAILSAFE_CODE)
BOOLEAN block_devio_read(DDRIVE *pdrive, dword blockno, byte * buf)
{
return(devio_read(pdrive->driveno, blockno, buf, (int) 1, FALSE));
}
BOOLEAN block_devio_write(BLKBUFF *pblk)
{
/* dword blockno;
blockno = pblk->blockno;*/
return(devio_write(pblk->pdrive->driveno,pblk->blockno, pblk->data, (int) 1, FALSE));
}
BOOLEAN fat_devio_write(DDRIVE *pdrive, FATBUFF *pblk, int fatnumber)
{
dword blockno;
if (fatnumber)
blockno = pblk->fat_blockno+pdrive->secpfat;
else
blockno = pblk->fat_blockno;
return(devio_write(pdrive->driveno,blockno, pblk->fat_data, (int) 1, FALSE));
}
BOOLEAN fat_devio_read(DDRIVE *pdrive, dword blockno, byte *fat_data)
{
return(devio_read(pdrive->driveno, blockno, fat_data, (int) 1, FALSE));
}
#else
/* Replacement routines provided by failsafe package */
BOOLEAN block_devio_read(DDRIVE *pdrive, dword blockno, byte * buf);
BOOLEAN block_devio_write(BLKBUFF *pblk);
BOOLEAN fat_devio_write(DDRIVE *pdrive, FATBUFF *pblk, int fatnumber);
BOOLEAN fat_devio_read(DDRIVE *pdrive, dword blockno, byte *fat_data);
#endif
/*****************************************************************************
pc_release_buf - Unlock a block buffer.
Description
Give back a buffer to the system buffer pool so that it may
be re-used. If was_err is TRUE this means that the data in the
buffer is invalid so discard the buffer from the buffer pool.
Returns
Nothing
***************************************************************************/
void pc_release_buf(BLKBUFF *pblk)
{
if (!pblk)
return;
DEBUG_CHECK_BLOCKS(pblk->pdrive->pbuffcntxt, pblk->pdrive->pbuffcntxt->num_blocks, "Release")
#if (DEBUG_BLOCK_CODE)
if (!pblk->pdrive->mount_valid)
debug_break("release buf", "Mount not valid");
if (pblk->block_state != DIRBLOCK_COMMITTED && pblk->block_state != DIRBLOCK_UNCOMMITTED)
debug_break("release buf", "releasing buffer not in use list");
#endif
if (pblk->block_state != DIRBLOCK_COMMITTED && pblk->block_state != DIRBLOCK_UNCOMMITTED)
return;
OS_CLAIM_FSCRITICAL()
if (pblk->use_count)
{
pblk->use_count -= 1;
if (!pblk->use_count)
pblk->pdrive->pbuffcntxt->num_free += 1;
}
OS_RELEASE_FSCRITICAL()
}
/*****************************************************************************
pc_discard_buf - Put a buffer back on the free list.
Description
Check if a buffer is in the buffer pool, unlink it from the
buffer pool if it is.
Put the buffer on the free list.
Returns
Nothing
***************************************************************************/
void pc_discard_buf(BLKBUFF *pblk)
{
BLKBUFFCNTXT *pbuffcntxt;
if (!pblk)
return;
/*note:pblk->pdrive must not be 0. if so, use pc_free_scratch_buffer() */
#if (DEBUG_BLOCK_CODE)
if (pblk->block_state == DIRBLOCK_FREE)
{
printf("Warning: discarding a free buffer \n");
}
if (pblk->block_state == DIRBLOCK_COMMITTED)
{ /* Could come from a disk free call */
printf("Warning discarding a committed buffer \n");
}
#endif
if (pblk->block_state == DIRBLOCK_FREE)
return;
OS_CLAIM_FSCRITICAL()
pbuffcntxt = pblk->pdrive->pbuffcntxt;
DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Discard 1")
if (pblk->block_state == DIRBLOCK_COMMITTED || pblk->block_state == DIRBLOCK_UNCOMMITTED)
{
/* Release it from the buffer pool */
pc_release_blk(pbuffcntxt, pblk);
#if (DEBUG_BLOCK_CODE)
if (pblk->pnext && pblk->pnext->pprev != pblk)
debug_break("discard buf", "Buffer and populated pool inconsistent");
if (pblk->pprev && pblk->pprev->pnext != pblk)
debug_break("discard buf", "Buffer and populated pool inconsistent");
#endif
/* Unlink it from the populated pool double check link integrity */
if (pblk->pnext && pblk->pnext->pprev == pblk)
pblk->pnext->pprev = pblk->pprev;
if (pblk->pprev && pblk->pprev->pnext == pblk)
pblk->pprev->pnext = pblk->pnext;
if (pbuffcntxt->ppopulated_blocks == pblk)
pbuffcntxt->ppopulated_blocks = pblk->pnext;
}
pblk->block_state = DIRBLOCK_FREE;
pblk->pnext = pbuffcntxt->pfree_blocks;
pbuffcntxt->num_free += 1;
pbuffcntxt->pfree_blocks = pblk;
OS_RELEASE_FSCRITICAL()
DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Discard 2")
}
/****************************************************************************
PC_READ_BLK - Allocate and read a BLKBUFF, or get it from the buffer pool.
Description
Use pdrive and blockno to determine what block to read. Read the block
or get it from the buffer pool and return the buffer.
Note: After reading, you own the buffer. You must release it by
calling pc_release_buff() or pc_discard_buff() before it may be
used for other blocks.
Returns
Returns a valid pointer or NULL if block not found and not readable.
*****************************************************************************/
BLKBUFF *pc_read_blk(DDRIVE *pdrive, dword blockno) /*__fn__*/
{
BLKBUFF *pblk;
BLKBUFFCNTXT *pbuffcntxt;
if ( !pdrive || (blockno >= pdrive->numsecs) )
return(0);
OS_CLAIM_FSCRITICAL()
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 1")
/* TRy to find it in the buffer pool */
pblk = pc_find_blk(pdrive, blockno);
if (pblk)
{
pdrive->pbuffcntxt->stat_cache_hits += 1;
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 2")
pblk->use_count += 1;
OS_RELEASE_FSCRITICAL()
}
else
{
pdrive->pbuffcntxt->stat_cache_misses += 1;
/* Allocate, read, stitch into the populated list, and the buffer pool */
pblk = pc_allocate_blk(pdrive, pdrive->pbuffcntxt);
OS_RELEASE_FSCRITICAL()
if (pblk)
{
if (block_devio_read(pdrive, blockno, pblk->data))
{
pbuffcntxt = pdrive->pbuffcntxt;
pblk->use_count = 1;
pblk->block_state = DIRBLOCK_UNCOMMITTED;
pblk->blockno = blockno;
pblk->pdrive = pdrive;
/* Put in front of populated list (it's new) */
OS_CLAIM_FSCRITICAL()
pblk->pprev = 0;
pblk->pnext = pbuffcntxt->ppopulated_blocks;
if (pbuffcntxt->ppopulated_blocks)
pbuffcntxt->ppopulated_blocks->pprev = pblk;
pbuffcntxt->ppopulated_blocks = pblk;
pc_add_blk(pbuffcntxt, pblk); /* Add it to the buffer pool */
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 3")
OS_RELEASE_FSCRITICAL()
}
else
{
/* set errno to IO error unless devio set PEDEVICE */
if (!get_errno())
rtfs_set_errno(PEIOERRORREADBLOCK); /* pc_read_blk device read error */
pc_discard_buf(pblk);
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 4")
pblk = 0;
}
}
}
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 5")
return(pblk);
}
/***************************************************************************
PC_SCRATCH_BLK - Return a block for scratch purposes.
Description
Use the block buffer pool as a heap of 512 byte memory locations
When done call void pc_free_scratch_buf(pblk) to clean up
Returns
Returns a blkbuf if one is available or NULL
****************************************************************************/
/* Allocate a scratch buffer from the shared (amongst drives) buffer pool */
BLKBUFF *pc_scratch_blk(void) /*__fn__*/
{
BLKBUFF *pblk;
OS_CLAIM_FSCRITICAL()
pblk = pc_allocate_blk(0, &prtfs_cfg->buffcntxt);
OS_RELEASE_FSCRITICAL()
return (pblk);
}
/* Free a scratch buffer to the shared (amongst drives) buffer pool */
void pc_free_scratch_blk(BLKBUFF *pblk)
{
BLKBUFFCNTXT *pbuffcntxt;
OS_CLAIM_FSCRITICAL()
pbuffcntxt = &prtfs_cfg->buffcntxt;
pblk->pnext = pbuffcntxt->pfree_blocks;
pbuffcntxt->pfree_blocks = pblk;
pblk->block_state = DIRBLOCK_FREE;
pbuffcntxt->num_free += 1;
OS_RELEASE_FSCRITICAL()
}
/***************************************************************************
PC_INIT_BLK - Zero a BLKBUFF and add it to the buffer pool
Description
Allocate and zero a BLKBUFF and add it to the to the buffer pool.
Note: After initializing you own the buffer. You must release it by
calling pc_release_buff() or pc_discard_buf() before it may be used
for other blocks.
Returns
Returns a valid pointer or NULL if no core.
****************************************************************************/
BLKBUFF *pc_init_blk(DDRIVE *pdrive, dword blockno) /*__fn__*/
{
BLKBUFF *pblk;
BLKBUFFCNTXT *pbuffcntxt;
if ( !pdrive || (blockno >= pdrive->numsecs) )
return(0);
/* Find it in the buffer pool */
OS_CLAIM_FSCRITICAL()
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Init 1")
pblk = pc_find_blk(pdrive, blockno);
if (pblk)
{
pblk->use_count += 1;
OS_RELEASE_FSCRITICAL()
}
else
{
/* Allocate, read, stitch into the populated list, and the buffer pool */
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Init 2")
pblk = pc_allocate_blk(pdrive, pdrive->pbuffcntxt);
OS_RELEASE_FSCRITICAL()
if (pblk)
{
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks-1, "Init 2_a")
pbuffcntxt = pdrive->pbuffcntxt;
pblk->use_count = 1;
pblk->block_state = DIRBLOCK_UNCOMMITTED;
pblk->blockno = blockno;
pblk->pdrive = pdrive;
/* Put in front of populated list (it's new) */
OS_CLAIM_FSCRITICAL()
pblk->pprev = 0;
pblk->pnext = pbuffcntxt->ppopulated_blocks;
if (pbuffcntxt->ppopulated_blocks)
pbuffcntxt->ppopulated_blocks->pprev = pblk;
pbuffcntxt->ppopulated_blocks = pblk;
pc_add_blk(pbuffcntxt, pblk); /* Add it to the buffer pool */
OS_RELEASE_FSCRITICAL()
DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Init 3")
}
}
if (pblk)
rtfs_memset(pblk->data, (byte) 0, 512);
return(pblk);
}
/****************************************************************************
PC_FREE_ALL_BLK - Release all buffers associated with a drive
Description
Use pdrive to find all buffers in the buffer pool associated with the
drive. Mark them as unused, called by dsk_close.
If any are locked, print a debug message in debug mode to warn the
programmer.
Returns
Nothing
****************************************************************************/
void pc_free_all_blk(DDRIVE *pdrive) /*__fn__*/
{
BLKBUFFCNTXT *pbuffcntxt;
BLKBUFF *pblk;
BOOLEAN deleting;
if (!pdrive)
return;
pbuffcntxt = pdrive->pbuffcntxt;
DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Free all 1")
do
{
deleting = FALSE;
OS_CLAIM_FSCRITICAL()
pblk = pbuffcntxt->ppopulated_blocks;
while (pblk)
{
if (pblk->pdrive == pdrive)
{
OS_RELEASE_FSCRITICAL()
pc_discard_buf(pblk);
OS_CLAIM_FSCRITICAL()
deleting = TRUE;
break;
}
else
pblk = pblk->pnext;
}
OS_RELEASE_FSCRITICAL()
} while (deleting);
DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Free all 2")
}
/***************************************************************************
PC_WRITE_BLK - Flush a BLKBUFF to disk.
Description
Use pdrive and blockno information in pblk to flush its data buffer
to disk.
Returns
Returns TRUE if the write succeeded.
****************************************************************************/
/* Write */
BOOLEAN pc_write_blk(BLKBUFF *pblk) /*__fn__*/
{
if (!block_devio_write(pblk))
{
/* set errno to IO error unless devio set PEDEVICE */
if (!get_errno())
rtfs_set_errno(PEIOERRORWRITEBLOCK); /* pc_write_blk device write error */
return(FALSE);
}
else
return(TRUE);
}
/* Add a block to the block buffer pool */
static void pc_add_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk)
{
int hash_index;
hash_index = (int) (pinblk->blockno&pbuffcntxt->hash_mask);
pinblk->pnext2 = *(pbuffcntxt->blk_hash_tbl+hash_index);
*(pbuffcntxt->blk_hash_tbl+hash_index) = pinblk;
}
/* Remove a block from the block buffer pool */
static void pc_release_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk)
{
int hash_index;
BLKBUFF *pblk;
hash_index = (int) (pinblk->blockno&pbuffcntxt->hash_mask);
pblk = *(pbuffcntxt->blk_hash_tbl+hash_index);
if (pblk == pinblk)
*(pbuffcntxt->blk_hash_tbl+hash_index) = pinblk->pnext2;
else
{
while (pblk)
{
if (pblk->pnext2==pinblk)
{
pblk->pnext2 = pinblk->pnext2;
break;
}
pblk = pblk->pnext2;
}
}
}
/* Find a block in the block buffer pool */
BLKBUFF *pc_find_blk(DDRIVE *pdrive, dword blockno)
{
BLKBUFFCNTXT *pbuffcntxt;
int hash_index;
BLKBUFF *pblk;
pbuffcntxt = pdrive->pbuffcntxt;
hash_index = (int) (blockno&pbuffcntxt->hash_mask);
pblk = *(pbuffcntxt->blk_hash_tbl+hash_index);
while (pblk)
{
if (pblk->blockno == blockno && pblk->pdrive == pdrive)
{
if (pblk->block_state == DIRBLOCK_UNCOMMITTED)
{ /* Put in front of populated list (it's the last touched) */
/* Unlink it from the populated pool */
if (pbuffcntxt->ppopulated_blocks != pblk)
{ /* Since we aren't the root we know pprev is good */
pblk->pprev->pnext = pblk->pnext; /* Unlink. */
if (pblk->pnext)
pblk->pnext->pprev = pblk->pprev;
pblk->pprev = 0; /* link in front*/
pblk->pnext = pbuffcntxt->ppopulated_blocks;
if (pbuffcntxt->ppopulated_blocks)
pbuffcntxt->ppopulated_blocks->pprev = pblk;
pbuffcntxt->ppopulated_blocks = pblk;
}
}
return(pblk);
}
pblk=pblk->pnext2;
}
return(0);
}
/* Allocate a block or re-use an un-committed one */
static BLKBUFF *pc_allocate_blk(DDRIVE *pdrive, BLKBUFFCNTXT *pbuffcntxt)
{
BLKBUFF *pblk, *pblkscan;
int num_free;
/* Note: pdrive may be NULL, do not dereference the pointer */
pblk = 0;
if (pbuffcntxt->pfree_blocks)
{
pblk = pbuffcntxt->pfree_blocks;
pbuffcntxt->pfree_blocks = pblk->pnext;
}
else if (pbuffcntxt->ppopulated_blocks)
{
/* Find the oldest UNCOMMITED block (deepest into the list) */
pblkscan = pbuffcntxt->ppopulated_blocks;
num_free = 0;
while (pblkscan)
{
if (pblkscan->block_state == DIRBLOCK_UNCOMMITTED && !pblkscan->use_count)
{
pblk = pblkscan;
num_free += 1;
}
pblkscan = pblkscan->pnext;
}
pbuffcntxt->num_free = num_free;
if (pblk)
{
pc_release_blk(pbuffcntxt, pblk); /* Remove it from buffer pool */
/* Unlink it from the populated pool */
if (pblk->pnext)
pblk->pnext->pprev = pblk->pprev;
if (pblk->pprev)
pblk->pprev->pnext = pblk->pnext;
if (pbuffcntxt->ppopulated_blocks == pblk)
pbuffcntxt->ppopulated_blocks = pblk->pnext;
}
}
if (pblk)
{ /* Put in a known state */
pbuffcntxt->num_free -= 1;
if (pbuffcntxt->num_free < pbuffcntxt->low_water)
pbuffcntxt->low_water = pbuffcntxt->num_free;
pblk->use_count = 0;
pblk->block_state = DIRBLOCK_ALLOCATED;
pblk->pdrive = pdrive;
}
else
{
pbuffcntxt->num_alloc_failures += 1;
rtfs_set_errno(PERESOURCEBLOCK); /* pc_allocate_blk out of resources */
}
return(pblk);
}
/* Tomo */
/* Traverse a cluster chain and make sure that all blocks in the cluster
chain are flushed from the buffer pool. This is required when deleting
a directory since it is possible, although unlikely, that blocks used in
the directory may be used in a file. This may cause the buffered
block to be different from on-disk block.
Called by pc_rmnode
*/
void pc_flush_chain_blk(DDRIVE *pdrive, CLUSTERTYPE cluster)
{
int i;
dword blockno;
BLKBUFF *pblk;
if ( cluster < 2 || cluster > pdrive->maxfindex)
return;
while (cluster != 0xffffffff) /* End of chain */
{
blockno = pc_cl2sector(pdrive, cluster);
if (blockno)
{
for (i = 0; i < pdrive->secpalloc; i++, blockno++)
{
/* Find it in the buffer pool */
OS_CLAIM_FSCRITICAL()
pblk = pc_find_blk(pdrive, blockno);
if (pblk)
pblk->use_count += 1;
OS_RELEASE_FSCRITICAL()
if (pblk)
{
pc_discard_buf(pblk);
}
}
}
/* Consult the fat for the next cluster */
cluster = FATOP(pdrive)->fatop_clnext(pdrive, cluster);
if (cluster == 0) /* clnext detected error */
break;
}
}
/* Initialize and populate a block buffer context structure */
BOOLEAN pc_initialize_block_pool(BLKBUFFCNTXT *pbuffcntxt, int nblkbuffs,
BLKBUFF *pmem_block_pool, int blk_hashtble_size, BLKBUFF **pblock_hash_table)
{
int i;
dword t;
BLKBUFF *pblk;
rtfs_memset(pbuffcntxt,(byte) 0, sizeof(BLKBUFFCNTXT));
rtfs_memset(pblock_hash_table,(byte) 0, sizeof(BLKBUFF *)*blk_hashtble_size);
pblk = pmem_block_pool;
for (i = 0; i < (nblkbuffs-1); i++, pblk++)
{
rtfs_memset(pblk,(byte) 0, sizeof(BLKBUFF));
pblk->pnext = pblk+1;
}
rtfs_memset(pblk,(byte) 0, sizeof(BLKBUFF));
/* pblk->pnext = 0; accomplished by memset */
pbuffcntxt->pfree_blocks = pmem_block_pool;
pbuffcntxt->hash_size = blk_hashtble_size;
/* Take size and subtract 1 to get the mask */
t = (dword) blk_hashtble_size;
pbuffcntxt->hash_mask = t-1;
pbuffcntxt->blk_hash_tbl = pblock_hash_table;
pbuffcntxt->num_blocks = nblkbuffs;
pbuffcntxt->num_free = nblkbuffs;
pbuffcntxt->low_water = nblkbuffs;
pbuffcntxt->num_alloc_failures = 0;
return(TRUE);
}
FATBUFF *pc_find_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, dword blockno);
static void pc_commit_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, FATBUFF *pblk);
void pc_commit_fat_table(FATBUFFCNTXT *pfatbuffcntxt);
void pc_sort_committed_blocks(FATBUFFCNTXT *pfatbuffcntxt);
BOOLEAN pc_flush_fat_blocks(DDRIVE *pdrive)
{
FATBUFFCNTXT *pfatbuffcntxt;
FATBUFF *pblk, *plast;
int hash_index;
dword b;
pfatbuffcntxt = &pdrive->fatcontext;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 1")
/* Make sure the primary cache is is sync with secondary */
pc_commit_fat_table(pfatbuffcntxt);
DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 2")
if (!pfatbuffcntxt->pcommitted_blocks)
return(TRUE);
/* Sort the blocks in ascending order */
DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 3")
pc_sort_committed_blocks(pfatbuffcntxt);
DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 4")
pblk = pfatbuffcntxt->pcommitted_blocks;
while (pblk)
{
if (!fat_devio_write(pdrive,pblk,0))
{
/* set errno to PEDEVICE unless devio set errno */
if (!get_errno())
rtfs_set_errno(PEIOERRORWRITEFAT); /* flush_fat device write error */
io_error:
pc_report_error(PCERR_FAT_FLUSH);
return(FALSE);
}
else
pblk = pblk->pnext;
}
if (pdrive->numfats >= 2)
{ /* Write the second copy */
pblk = pfatbuffcntxt->pcommitted_blocks;
while (pblk)
{
if (!fat_devio_write(pdrive,pblk,1))
{
/* set errno to PEDEVICE unless devio set errno */
if (!get_errno())
rtfs_set_errno(PEIOERRORWRITEFAT); /* flush_fat device write error */
goto io_error;
}
else
pblk = pblk->pnext;
}
}
/* Set all entries uncommitted and put them in front of uncommitted list */
DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 5")
plast = 0;
pblk = pfatbuffcntxt->pcommitted_blocks;
while (pblk)
{
pblk->fat_block_state = FATBLOCK_UNCOMMITTED;
/* Clear from the primary cache if needed */
hash_index = (int) (pblk->fat_blockno&pfatbuffcntxt->hash_mask);
b = *(pfatbuffcntxt->mapped_blocks+hash_index);
if ( (b & 0x0ffffffful) == pblk->fat_blockno)
{
b &= 0x3ffffffful; /* Clear 0x40000000ul and 0x80000000ul*/
*(pfatbuffcntxt->mapped_blocks+hash_index) = b;
}
plast = pblk;
pblk = pblk->pnext;
}
if (pfatbuffcntxt->puncommitted_blocks)
pfatbuffcntxt->puncommitted_blocks->pprev = plast;
plast->pnext = pfatbuffcntxt->puncommitted_blocks;
/* pfatbuffcntxt->pcommitted_blocks->pprev = 0; - this is already true*/
pfatbuffcntxt->puncommitted_blocks = pfatbuffcntxt->pcommitted_blocks;
pfatbuffcntxt->pcommitted_blocks = 0;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 6")
return(TRUE);
}
byte *pc_map_fat_block(DDRIVE *pdrive, dword blockno, dword usage_flags)
{
int hash_index, temp_hash_index;
FATBUFFCNTXT *pfatbuffcntxt;
FATBUFF *pblk, *pblkscan;
dword b;
pfatbuffcntxt = &pdrive->fatcontext;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 1")
hash_index = (int) (blockno&pfatbuffcntxt->hash_mask);
b = *(pfatbuffcntxt->mapped_blocks+hash_index);
if ( (b & 0x0ffffffful) == blockno)
{
pfatbuffcntxt->stat_primary_cache_hits += 1;
#if (INCLUDE_FAILSAFE_CODE)
/* If in the primary cache and the new flag is write but the old
flag was not write then we will need to journal the block */
if ((usage_flags & 0x80000000ul) && (!(b & 0x80000000ul)))
if (pro_failsafe_journal_full(pdrive))
return(0); /* Failsafe sets errno */
#endif
if (usage_flags) /* bit 31 0x80000000ul is write bit 29 0x20000000ul is lock */
*(pfatbuffcntxt->mapped_blocks+hash_index) |= usage_flags;
return (*(pfatbuffcntxt->mapped_data+hash_index));
}
else
{ /* Swap out of the primary cache */
if ( (b & 0xc0000000ul) == 0x80000000ul)
{ /* Move to commit list if state changed from not_for_write to for_write */
pblk = pc_find_fat_blk(pfatbuffcntxt, (b & 0x0ffffffful));
if (pblk) /* This should not fail */
pc_commit_fat_blk(pfatbuffcntxt, pblk);
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 2")
*(pfatbuffcntxt->mapped_blocks+hash_index) = 0;
}
/* See if blockno is in the secondary cache */
pblk = pc_find_fat_blk(pfatbuffcntxt, blockno);
if (pblk)
{
pfatbuffcntxt->stat_secondary_cache_hits += 1;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 3")
b = blockno;
if (pblk->fat_block_state == FATBLOCK_COMMITTED)
{
b |= 0x40000000ul;
}
#if (INCLUDE_FAILSAFE_CODE)
else
{
/* If in the primary cache but uncommitted, if it will
switch to committed now then we will have to journal the block */
if (usage_flags & 0x80000000ul)
{
if (pro_failsafe_journal_full(pdrive))
return(0); /* Failsafe sets errno */
}
}
#endif
if (usage_flags) /* bit 31 0x80000000ul is write bit 29 0x20000000ul is lock */
b |= usage_flags;
*(pfatbuffcntxt->mapped_blocks+hash_index) = b;
*(pfatbuffcntxt->mapped_data+hash_index) = pblk->fat_data;
return(pblk->fat_data);
}
/* Not in secondary. Are there any free ? */
if (!pfatbuffcntxt->pfree_blocks)
{
/* Make sure commit state changes in the fat table are up to date */
/* Since hhere may committed blocks in the primary, but not secondary */
pc_commit_fat_table(pfatbuffcntxt);
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 4")
if (!pfatbuffcntxt->puncommitted_blocks)
{ /* No uncommitted blocks left */
/* Write the oldest committed block to disk */
/* So we can re-use the buffer space for another block */
pblk = pfatbuffcntxt->pcommitted_blocks;
while (pblk && pblk->pnext) /* Go to end of list */
{
pblk = pblk->pnext;
}
if (pblk)
{ /* Found one */
pfatbuffcntxt->stat_secondary_cache_swaps += 1;
if (!fat_devio_write(pdrive,pblk,0))
{
swap_write_error:
if (!get_errno())
rtfs_set_errno(PEIOERRORWRITEFAT); /* pc_map_fat_block device write error */
return(0);
}
if (pdrive->numfats >= 2)
{ /* Write the second copy */
if (!fat_devio_write(pdrive,pblk,1))
goto swap_write_error;
}
/* Remove it from committed list */
if (pblk->pnext)
pblk->pnext->pprev = pblk->pprev;
if (pblk->pprev)
pblk->pprev->pnext = pblk->pnext;
if (pblk == pfatbuffcntxt->pcommitted_blocks)
pfatbuffcntxt->pcommitted_blocks = pblk->pnext;
pfatbuffcntxt->stat_secondary_cache_swaps += 1;
}
}
else
{
/* Find the oldest UNCOMMITED block (deepest into the list) */
pblk = pfatbuffcntxt->puncommitted_blocks;
while (pblk && pblk->pnext) /* Go to end of list */
{
pblk = pblk->pnext;
}
if (pblk)
{ /* Found one */
/* Remove it from uncommitted list */
if (pblk->pnext)
pblk->pnext->pprev = pblk->pprev;
if (pblk->pprev)
pblk->pprev->pnext = pblk->pnext;
if (pblk == pfatbuffcntxt->puncommitted_blocks)
pfatbuffcntxt->puncommitted_blocks = pblk->pnext;
}
}
if (pblk) /* Came from either the committed or uncommited list */
{
/* Clear from the primary cache if needed */
temp_hash_index = (int) (pblk->fat_blockno&pfatbuffcntxt->hash_mask);
b = *(pfatbuffcntxt->mapped_blocks+temp_hash_index);
if ( (b & 0x0ffffffful) == pblk->fat_blockno)
*(pfatbuffcntxt->mapped_blocks+temp_hash_index) = 0;
/* Now get it out of the hash table */
if (pblk == *(pfatbuffcntxt->fat_blk_hash_tbl+temp_hash_index))
*(pfatbuffcntxt->fat_blk_hash_tbl+temp_hash_index) = pblk->pnext2;
else
{
pblkscan = *(pfatbuffcntxt->fat_blk_hash_tbl+temp_hash_index);
while (pblkscan)
{
if (pblkscan->pnext2==pblk)
{
pblkscan->pnext2 = pblk->pnext2;
break;
}
pblkscan = pblkscan->pnext2;
}
}
/* Put it on the free list */
pblk->pnext = pfatbuffcntxt->pfree_blocks;
pfatbuffcntxt->pfree_blocks = pblk;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 5")
}
}
else
{ /* the free list already had a block available for us */
pfatbuffcntxt->stat_secondary_cache_loads += 1;
}
pblk = pfatbuffcntxt->pfree_blocks;
if (!pblk)
{
/* No blocks available. We tried to flush the secondary and
that didn't come up with any so we're completely out */
rtfs_set_errno(PERESOURCEFATBLOCK);
return(0);
}
else if (fat_devio_read(pdrive, blockno, pblk->fat_data))
{ /* The read worked. Unhook from the free list */
pfatbuffcntxt->stat_secondary_cache_loads += 1;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 6")
pfatbuffcntxt->pfree_blocks = pblk->pnext;
pblk->fat_block_state = FATBLOCK_UNCOMMITTED;
pblk->fat_blockno = blockno;
/* Move to the front of the list so we don't swap too soon */
if (pfatbuffcntxt->puncommitted_blocks)
pfatbuffcntxt->puncommitted_blocks->pprev = pblk;
pblk->pprev = 0;
pblk->pnext = pfatbuffcntxt->puncommitted_blocks;
pfatbuffcntxt->puncommitted_blocks = pblk;
/* Insert in the hash table */
pblk->pnext2 = *(pfatbuffcntxt->fat_blk_hash_tbl+hash_index);
*(pfatbuffcntxt->fat_blk_hash_tbl+hash_index) = pblk;
if (usage_flags) /* bit 31 0x80000000ul is write bit 29 0x20000000ul is lock */
blockno |= usage_flags;
*(pfatbuffcntxt->mapped_blocks+hash_index) = blockno;
*(pfatbuffcntxt->mapped_data+hash_index) = pblk->fat_data;
DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 7")
#if (INCLUDE_FAILSAFE_CODE)
/* We are coming from the FREE state, but write is
set so journal the buffer */
if (usage_flags & 0x80000000ul)
{
if (pro_failsafe_journal_full(pdrive))
return(0); /* Failsafe sets errno */
}
#endif
return(pblk->fat_data);
}
else
{
if (!get_errno())
rtfs_set_errno(PEIOERRORREADFAT); /* pc_map_fat_block device write error */
return(0);
}
}
}
BOOLEAN pc_initialize_fat_block_pool(FATBUFFCNTXT *pfatbuffcntxt,
int fat_buffer_size, FATBUFF *pfat_buffers,
int fat_hashtbl_size, FATBUFF **pfat_hash_table,
byte **pfat_primary_cache, dword *pfat_primary_index)
{
dword t;
pfatbuffcntxt->pfat_buffers = pfat_buffers; /* save the address of buffer pool */
pfatbuffcntxt->mapped_data = pfat_primary_cache;
pfatbuffcntxt->mapped_blocks = pfat_primary_index;
pfatbuffcntxt->fat_blk_hash_tbl = pfat_hash_table;
pfatbuffcntxt->hash_size = fat_hashtbl_size;
/* Take size and subtract 1 to get the mask */
t = (dword) fat_hashtbl_size;
pfatbuffcntxt->hash_mask = t-1;
pfatbuffcntxt->num_blocks = fat_buffer_size;
/* Now clear it to the NO cached state */
pc_free_all_fat_blocks(pfatbuffcntxt);
return(TRUE);
}
/* Reset it fat buffer cache */
void pc_free_all_fat_blocks(FATBUFFCNTXT *pfatbuffcntxt)
{
int i;
FATBUFF *pblk;
/* Clear stats */
pfatbuffcntxt->stat_primary_cache_hits =
pfatbuffcntxt->stat_secondary_cache_hits =
pfatbuffcntxt->stat_secondary_cache_loads =
pfatbuffcntxt->stat_secondary_cache_swaps = 0;
/* Clear hash tables */
rtfs_memset(pfatbuffcntxt->fat_blk_hash_tbl,0, sizeof(FATBUFF *)*pfatbuffcntxt->hash_size);
rtfs_memset(pfatbuffcntxt->mapped_data,(byte) 0, sizeof(byte *)*pfatbuffcntxt->hash_size);
rtfs_memset(pfatbuffcntxt->mapped_blocks,(byte) 0, sizeof(dword)*pfatbuffcntxt->hash_size);
/* Reset block lists */
pfatbuffcntxt->puncommitted_blocks = 0;
pfatbuffcntxt->pcommitted_blocks = 0;
/* Regenerate the free list */
pfatbuffcntxt->pfree_blocks = pfatbuffcntxt->pfat_buffers;
pblk = pfatbuffcntxt->pfat_buffers;
for (i = 0; i < (pfatbuffcntxt->num_blocks-1); i++, pblk++)
{
rtfs_memset(pblk,(byte) 0, sizeof(FATBUFF));
pblk->pnext = pblk+1;
}
rtfs_memset(pblk,(byte) 0, sizeof(FATBUFF));
pblk->pnext = 0;
}
FATBUFF *pc_find_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, dword blockno)
{
int hash_index;
FATBUFF *pblk;
hash_index = (int) (blockno&pfatbuffcntxt->hash_mask);
pblk = *(pfatbuffcntxt->fat_blk_hash_tbl+hash_index);
while (pblk)
{
if (pblk->fat_blockno == blockno)
return(pblk);
else
pblk=pblk->pnext2;
}
return(0);
}
static void pc_commit_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, FATBUFF *pblk)
{
#if (DEBUG_FAT_CODE)
if (pblk->fat_block_state != FATBLOCK_UNCOMMITTED)
debug_break("commit fat block", "Not un-committed");
#endif
/* Remove from the uncommitted list */
if (pblk->pnext)
pblk->pnext->pprev = pblk->pprev;
if (pblk->pprev)
pblk->pprev->pnext = pblk->pnext;
if (pfatbuffcntxt->puncommitted_blocks == pblk)
pfatbuffcntxt->puncommitted_blocks = pblk->pnext;
/* Put on the committed list */
pblk->fat_block_state = FATBLOCK_COMMITTED;
pblk->pprev = 0;
if (pfatbuffcntxt->pcommitted_blocks)
pfatbuffcntxt->pcommitted_blocks->pprev = pblk;
pblk->pnext = pfatbuffcntxt->pcommitted_blocks;
pfatbuffcntxt->pcommitted_blocks = pblk;
}
void pc_commit_fat_table(FATBUFFCNTXT *pfatbuffcntxt)
{
dword b;
int i;
FATBUFF *pblk;
for (i = 0; i < pfatbuffcntxt->hash_size; i++)
{
b = *(pfatbuffcntxt->mapped_blocks+i);
if ( b && ((b & 0xc0000000ul) == 0x80000000ul) )
{ /* It changed to committed */
/* Move from uncommitted list to committed list */
pblk = pc_find_fat_blk(pfatbuffcntxt, (b & 0x0ffffffful));
if (pblk) /* This should not fail */
pc_commit_fat_blk(pfatbuffcntxt, pblk);
b |= 0x40000000ul; /* set commited flag in primary */
*(pfatbuffcntxt->mapped_blocks+i) = b;
}
}
}
void pc_sort_committed_blocks(FATBUFFCNTXT *pfatbuffcntxt)
{
FATBUFF *pblk, *pprev, *psorted_list, *psort, *pblk_source_scan;
/* The first element is the root of the sorted list, we begin from the
next element scanning foreward and inserting in sorted order */
psorted_list = pfatbuffcntxt->pcommitted_blocks; /* pfatbuffcntxt->pcommitted_blocks is guaranteed not null */
pblk_source_scan = pfatbuffcntxt->pcommitted_blocks->pnext;
if (pblk_source_scan)
{
psorted_list->pnext = 0;
psorted_list->pprev = 0;
pblk_source_scan->pprev = 0;
}
while (pblk_source_scan)
{
pblk = pblk_source_scan;
pblk_source_scan = pblk_source_scan->pnext;
pblk->pnext = 0;
pprev = 0;
psort = psorted_list;
/* Find the first block in the sorted list with blockno > the new entry */
while (psort && psort->fat_blockno < pblk->fat_blockno)
{
pprev = psort;
psort = psort->pnext;
}
if (pprev)
{ /* Insert the new one after pprev */
if (pprev->pnext)
pprev->pnext->pprev = pblk;
pblk->pprev = pprev;
pblk->pnext = pprev->pnext;
pprev->pnext = pblk;
}
else
{ /* We are the smallest */
if (psorted_list)
psorted_list->pprev = pblk;
pblk->pprev = 0;
pblk->pnext = psorted_list;
psorted_list = pblk;
}
}
pfatbuffcntxt->pcommitted_blocks = psorted_list;
}
#if (DEBUG_BLOCK_CODE || DEBUG_FAT_CODE)
void debug_break(char *where, char *message)
{
printf("%s: %s\n", where, message);
}
#endif
#if (DEBUG_FAT_CODE)
void debug_check_fat(FATBUFFCNTXT *pfatbuffcntxt, char *where)
{
FATBUFF *pblk;
FATBUFF *pblk_prev;
int nb = 0;
int numblocks;
numblocks = pfatbuffcntxt->num_blocks;
pblk = pfatbuffcntxt->pfree_blocks;
while (pblk)
{
nb += 1;
if (nb > numblocks)
debug_break(where, "Bad Fat freelist");
pblk = pblk->pnext;
}
pblk = pfatbuffcntxt->pcommitted_blocks;
if (pblk && pblk->pprev)
debug_break(where, "Bad fat committed root");
while (pblk)
{
nb += 1;
if (nb > numblocks)
debug_break(where, "Bad fat committed list");
if (pblk->fat_block_state != FATBLOCK_COMMITTED)
debug_break(where, "Uncommitted blokc on committed list");
pblk = pblk->pnext;
}
if (pfatbuffcntxt->pcommitted_blocks)
{
pblk_prev = pblk = pfatbuffcntxt->pcommitted_blocks;
pblk = pblk->pnext;
while (pblk)
{
if (pblk->pprev != pblk_prev)
debug_break(where, "Bad link in committed list");
pblk_prev = pblk;
pblk = pblk->pnext;
}
}
pblk = pfatbuffcntxt->puncommitted_blocks;
if (pblk && pblk->pprev)
debug_break(where, "Bad fat uncommitted root");
while (pblk)
{
nb += 1;
if (nb > numblocks)
debug_break(where, "Bad fat uncommitted list");
if (pblk->fat_block_state != FATBLOCK_UNCOMMITTED)
debug_break(where, "Uncommitted blokc on committed list");
pblk = pblk->pnext;
}
if (pfatbuffcntxt->puncommitted_blocks)
{
pblk_prev = pblk = pfatbuffcntxt->puncommitted_blocks;
pblk = pblk->pnext;
while (pblk)
{
if (pblk->pprev != pblk_prev)
debug_break(where, "Bad link in uncommitted list");
pblk_prev = pblk;
pblk = pblk->pnext;
}
}
if (nb != numblocks)
debug_break(where, "Fat Leak");
}
#endif /* DEBUG_FAT_CODE */
#if (DEBUG_BLOCK_CODE)
void debug_check_blocks(BLKBUFFCNTXT *pbuffcntxt, int numblocks, char *where)
{
BLKBUFF *pblk;
BLKBUFF *pblk_prev;
int nb = 0;
int i;
pblk = pbuffcntxt->pfree_blocks;
while (pblk)
{
nb += 1;
if (nb > numblocks)
debug_break(where, "Bad freelist");
pblk = pblk->pnext;
}
pblk = pbuffcntxt->ppopulated_blocks;
if (pblk && pblk->pprev)
debug_break(where, "Bad populated root");
while (pblk)
{
nb += 1;
if (nb > numblocks)
debug_break(where, "Bad populated list");
pblk = pblk->pnext;
}
if (nb != numblocks)
debug_break(where, "Leak");
if (pbuffcntxt->ppopulated_blocks)
{
pblk_prev = pblk = pbuffcntxt->ppopulated_blocks;
pblk = pblk->pnext;
while (pblk)
{
if (pblk->pprev != pblk_prev)
debug_break(where, "Bad link in populated list");
pblk_prev = pblk;
pblk = pblk->pnext;
}
}
for (i = 0; i < pbuffcntxt->hash_size; i++)
{
nb = 0;
pblk = *(pbuffcntxt->blk_hash_tbl+i);
while (pblk)
{
if (i != (int) (pblk->blockno&pbuffcntxt->hash_mask))
debug_break(where, "Block in wrong hash slot");
nb += 1;
if (nb > numblocks)
debug_break(where, "Loop in hash table");
pblk = pblk->pnext2;
}
}
}
#endif /* (DEBUG_BLOCK_CODE) */