📄 block.c
字号:
/*
* EBS - RTFS (Real Time File Manager)
*
* Copyright Peter Van Oudenaren , 1993
* All rights reserved.
* This code may not be redistributed in source or linkable object form
* without the consent of its author.
*/
/* BLOCK.C - Directory block buffering routines
Block buffers contain a data block a DDRIVE pointer and a block number.
If the DDRIVE pointer is Non Null the data is valid for the block.
Blocks have three states.
Unused - May be grabbed to use on a block. (PDRIVE == NULL)
Locked - May not be re-used until the routine using it lets go
Unlocked- Data is valid in the buffer but no one is using it.
if there are no Unused blocks this one may be used.
But if someone needs the data in here it will be relocked
and used.
Routines in this file include:
pc_alloc_blk - Internal to this file.
pc_blkpool - Internal to this file.
pc_free_all_blk - Release all buffers associated with a drive
pc_free_buf - Release a single buffer for possible re-use
pc_init_blk - Initialize a buffer's drive and block #s. Zero its
data buffer
pc_read_blk - Read data from disk into a buffer
pc_write_blk - Write data from the buffer to the disk.
*/
#include "pcdisk.h"
IMPORT BLKBUFF *mem_block_pool;
IMPORT _PC_BDEVSW pc_bdevsw[];
/******************************************************************************
PC_ALLOC_BLK - Find existing or create an empty block in the buffer pool.
Description
Use pdrive and blockno to search for a buffer in the buffer pool.
If not found create a new buffer entry by discarding the Least recently
used buffer in the buffer pool. The buffer is locked in core. A pointer to
the buffer is returned in ppblk. If all buffers are in use it returns
NULL in *ppblk.
Returns
Returns YES if the buffer was found in the pool or NO if a new
buffer was assigned.
*****************************************************************************/
/* This function is used only by functions in this file */
BOOL pc_alloc_blk(BLKBUFF **ppblk, DDRIVE *pdrive, BLOCKT blockno) /*__fn__*/
{
BLKBUFF *pblk;
BLKBUFF *oldest = NULL;
BLKBUFF *freeblk = NULL;
ULONG lru = (ULONG) ~0L;
LOCAL ULONG useindex = 0L;
/* Get or init the block pool */
pblk = pc_blkpool(pdrive);
useindex += 1;
while (pblk)
{
if (!pblk->pdrive)
{
/* This buffer's free */
freeblk = pblk; /* BUG FIX */
}
else
{
if ( (pblk->pdrive == pdrive) && (pblk->blockno == blockno) )
{
/* Found it */
*ppblk = pblk;
/* Update the last recently used stuf */
pblk->lru = useindex;
pblk->use_count += 1;
return(YES);
}
else
{
/* No match. see if its a candidate for swapping if we run out of
pointers */
if (!pblk->use_count)
{
if (pblk->lru < lru)
{
lru = pblk->lru;
oldest = pblk;
}
}
}
}
pblk = pblk->pnext;
}
/* If off the end of the list we have to bump somebody */
if (freeblk)
pblk = freeblk;
else
pblk = oldest;
if (!pblk)
{
pc_report_error(PCERR_BLOCKCLAIM);
/* Panic */
*ppblk = NULL;
return(NO);
}
pblk->lru = useindex;
pblk->pdrive = pdrive;
pblk->blockno = blockno;
pblk->use_count = 1;
pblk->io_pending = NO;
pblk->io_error = NO;
*ppblk = pblk;
/* Return NO since we didn't find it in the buffer pool */
return (NO);
}
/****************************************************************************
PC_BLKBOOL - Return the first element in a drives buffer pool
Description
Return the beginning of the buffer pool for a drive. If the pool
is uninitialized report it. The buffer pool should have been
initialized in pc_memory_init.
Returns
Returns the beginning of the buffer pool or NULL if pc_memory_init was
not first called.
****************************************************************************/
/* Return the beginning of the buffer pool. Report an error if not already
initialized */
BLKBUFF *pc_blkpool(DDRIVE *pdrive) /*__fn__*/
{
pdrive = pdrive; /* Make compilers happy */
/* If not initialized somebody didn't call pc_meminit */
if (!mem_block_pool)
pc_report_error(PCERR_BLOCKALLOC);
return(mem_block_pool);
}
/****************************************************************************
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__*/
{
BLKBUFF *pblk;
/* Get or init the block pool */
pblk = pc_blkpool(pdrive);
while (pblk)
{
if (pblk->pdrive == pdrive)
{
if (pblk->use_count)
pc_report_error(PCERR_BLOCKLOCK);
pc_free_buf(pblk, YES);
}
pblk = pblk->pnext;
}
}
/*****************************************************************************
PC_FREE_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 YES this means that the data in the
buffer is invalid so discard the buffer from the buffer pool.
Returns
Nothing
***************************************************************************/
/* Free a buffer by unlocking it. If waserr is true, zero out the
drive number so the erroneous data is not cached. */
VOID pc_free_buf(BLKBUFF *pblk, BOOL waserr) /*__fn__*/
{
if (pblk)
{
if (pblk->use_count)
pblk->use_count -= 1;
/* If the buffer is corrupted we null the buffer. This is safe even
in a multitasking environment because the region of the disk
containing the block is always locked exclusively when buffer
writes are taking place */
if (waserr)
pblk->pdrive = NULL;
}
}
/***************************************************************************
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_free_buff() before it may be used for other blocks.
Returns
Returns a valid pointer or NULL if no core.
****************************************************************************/
BLKBUFF *pc_init_blk(DDRIVE *pdrive, BLOCKT blockno) /*__fn__*/
{
BLKBUFF *pblk;
if ( !pdrive || (blockno >= pdrive->numsecs) )
return(NULL);
else
{
pc_alloc_blk(&pblk, pdrive , blockno );
if (!pblk)
return(NULL);
pc_memfill(pblk->data, 512, '\0');
return (pblk);
}
}
/****************************************************************************
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_free_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, BLOCKT blockno) /*__fn__*/
{
BLKBUFF *pblk;
BOOL found_buffer;
if ( !pdrive || (blockno >= pdrive->numsecs) )
return(NULL);
found_buffer = pc_alloc_blk(&pblk, pdrive , blockno);
if (!pblk)
return(NULL); /* Internal error */
if (found_buffer)
{
/* If the block is being read we loop until the io_pending
condition is gone. */
#if (LOCK_METHOD == 2)
while (pblk->io_pending)
{
fs_release();
fs_suspend_task();
fs_reclaim();
}
#endif
/* If the error flag is set in the block we return error. If we're the
last one to sense the error we free the buffer */
if (pblk->io_error)
{
pblk->use_count -= 1;
if (!pblk->use_count)
{
/* If we are the last request null out the buffer */
pblk->pdrive = NULL;
}
pblk = NULL;
}
}
else
{
/* Not found. Read it in */
pblk->io_pending = YES;
pblk->io_error = NO;
PC_DRIVE_IO_ENTER(pdrive->driveno)
if (!pc_bdevsw[pdrive->driveno].io_proc(pdrive->driveno, blockno, pblk->data, (COUNT) 1, YES))
{
/* oops: Drop the use count and mark an error.
if anybody else is waiting do a wakeup */
pblk->io_pending = NO;
pblk->use_count -= 1;
pblk->io_error = YES;
if (!pblk->use_count)
pblk->pdrive = NULL; /* Now the block doesn't exist
in the buffer pool */
pblk = NULL;
}
else
{
/* The read worked */
pblk->io_pending = NO;
}
PC_DRIVE_IO_EXIT(pdrive->driveno)
}
return (pblk);
}
/***************************************************************************
PC_WRITE_BLK - Flush a BLKBUFF to disk.
Description
Use pdrive and blockno information in pblk to flush it's data buffer
to disk.
Returns
Returns YES if the write succeeded.
****************************************************************************/
/* Write */
BOOL pc_write_blk(BLKBUFF *pblk) /*__fn__*/
{
BOOL ret_val;
if (!pblk || !pblk->pdrive)
return(NO);
PC_DRIVE_IO_ENTER(pblk->pdrive->driveno)
ret_val = pc_bdevsw[pblk->pdrive->driveno].io_proc(pblk->pdrive->driveno,pblk->blockno, pblk->data, (COUNT) 1, NO);
PC_DRIVE_IO_EXIT(pblk->pdrive->driveno)
return(ret_val);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -