📄 buffer.c
字号:
DEBUGALLOC(sizeof(BUF));
pbuf->b_pdata = pbCarve;
pbuf->b_flags |= BUF_FREE;
pbuf->b_pvol = pvol;
pbCarve += pvol->v_pdsk->d_diActive.di_bytes_per_sect;
AddItem((PDLINK)&pvol->v_dlBufMRU, (PDLINK)&pbuf->b_dlink);
}
// Since we have at least MIN_BUFFERS buffers now, calculate
// the number of threads our buffer pool can handle simultaneously.
// If DEMAND_PAGING is enabled, we reduce that number by 1 so that
// a Pagein thread can always assume there are adequate buffers.
#ifdef DEMAND_PAGING
pvol->v_cBufThreads = pvol->v_cbufTotal / MIN_BUFFERS - 1;
#else
//32/4=8
pvol->v_cBufThreads = pvol->v_cbufTotal / MIN_BUFFERS;
#endif
}
// For all calls (first time or not), we must invalidate any buffers
// we retained for an unmounted volume that was subsequently *recycled*
// instead of *remounted*.
if (pvol->v_flags & VOLF_RECYCLED)
InvalidateBufferSet(pvol, TRUE);
LeaveCriticalSection(&pvol->v_csBuffers);
return TRUE;
}
/* FreeBufferPool - free all buffers in buffer pool
*
* ENTRY
* pvol - pointer to VOLUME currently being unmounted
*
* EXIT
* TRUE if volume can be freed, or FALSE if not (ie, there are still
* dirty buffers that could not be committed). Note that if the volume
* wasn't going to be freed to begin with, we always return FALSE.
*
* This module keeps track of the number of buffer clients (volumes),
* and only when the last client has called FreeBufferPool will the pool
* actually be freed (subject to all buffers being clean, of course).
*/
BOOL FreeBufferPool(PVOLUME pvol)
{
PBUF pbuf, pbufEnd;
ASSERT(OWNCRITICALSECTION(&pvol->v_cs));
// If the current volume isn't even using the buffer pool (yet),
// then we're done.
if (!(pvol->v_flags & VOLF_BUFFERED))
return TRUE;
EnterCriticalSection(&pvol->v_csBuffers);
// This is our last chance to commit all dirty buffers. With any
// luck, this will clean all the buffers, allowing us to free them all.
// That will be our assumption at least. If dirty buffers still remain
// for this volume, its DIRTY bit will get set again, below.
CommitVolumeBuffers(pvol);
pvol->v_flags &= ~VOLF_DIRTY;
// If we have buffers carved from a single chunk of memory, then
// quickly walk the entire list and determine if ALL the carved buffers
// can be freed. A single dirty or held carved buffer means that NONE
// of the carved buffers can be freed.
pbuf = pvol->v_dlBufMRU.pbufNext;
pbufEnd = (PBUF)&pvol->v_dlBufMRU;
DEBUGFREE(pvol->v_cbufTotal * pvol->v_pdsk->d_diActive.di_bytes_per_sect);
VERIFYTRUE(VirtualFree(pvol->v_pbBufCarve, 0, MEM_RELEASE));
pvol->v_pbBufCarve = NULL;
// Now walk the buffer list again, freeing every buffer that we can.
pbuf = pvol->v_dlBufMRU.pbufNext;
while (pbuf != pbufEnd) {
// The only reason we hold the buffer is so that CleanBuffer
// won't generate a bogus assertion. We don't care about the
// data anymore, so it is otherwise pointless.
HoldBuffer(pbuf);
CleanBuffer(pbuf);
UnholdBuffer(pbuf);
ASSERT(pbuf->b_pvol == NULL || pbuf->b_pvol == pvol);
pbuf->b_flags &= BUF_FREE;
// The current buffer is clean. Now, if this is NOT a carved
// buffer, or it is but we can free all the carved buffers anyway,
// then free the current buffer.
pvol->v_cbufTotal--;
RemoveItem((PDLINK)&pbuf->b_dlink);
DEBUGFREE(sizeof(BUF));
VERIFYTRUE(HeapFree(hHeap, 0, pbuf));
pbuf = pvol->v_dlBufMRU.pbufNext;
continue;
pbuf = pbuf->b_dlink.pbufNext;
}
// If there were no dirty buffers for this volume, then we can remove
// its reference to buffer pool.
if (!(pvol->v_flags & VOLF_DIRTY)) {
pvol->v_flags &= ~VOLF_BUFFERED;
}
LeaveCriticalSection(&pvol->v_csBuffers);
return TRUE;
}
/* ModifyBuffer - Prepare to dirty buffer
*
* ENTRY
* pbuf - pointer to buffer
* pMod - pointer to modification
* cbMod - length of modification (ZERO to prevent logging)
*
* EXIT
* ERROR_SUCCESS if the buffer is allowed to be modified, otherwise
* some error code (eg, ERROR_WRITE_PROTECT).
*/
DWORD ModifyBuffer(PBUF pbuf, PVOID pMod, int cbMod)
{
DWORD dwError = ERROR_SUCCESS;
ASSERTHELDBUFFER(pbuf);
if (pbuf->b_pvol->v_flags & VOLF_READONLY) {
dwError = ERROR_WRITE_PROTECT;
}
if (!dwError)
DirtyBuffer(pbuf);
return dwError;
}
/* DirtyBuffer - Dirty a buffer
*
* ENTRY
* pbuf - pointer to BUF
*
* EXIT
* The buffer is marked DIRTY.
*/
void DirtyBuffer(PBUF pbuf)
{
ASSERTHELDBUFFER(pbuf);
pbuf->b_flags |= BUF_DIRTY;
}
/* DirtyBufferError - Dirty a buffer that cannot be cleaned
*
* ENTRY
* pbuf - pointer to BUF
* pMod - pointer to modification (NULL if none)
* cbMod - length of modification (ZERO if none)
*
* EXIT
* The buffer is marked DIRTY, and any error is recorded.
*/
void DirtyBufferError(PBUF pbuf, PVOID pMod, int cbMod)
{
PVOLUME pvol = pbuf->b_pvol;
ASSERT (pvol);
ASSERTHELDBUFFER(pbuf);
pbuf->b_flags |= BUF_DIRTY;
EnterCriticalSection(&pvol->v_csBuffers);
if (!(pbuf->b_flags & BUF_ERROR)) {
if (pvol->v_cbufError % MIN_BUFFERS == 0)
InterlockedDecrement(&pvol->v_cBufThreads);
pvol->v_cbufError++;
pbuf->b_flags |= BUF_ERROR;
}
LeaveCriticalSection(&pvol->v_csBuffers);
}
/* CommitBuffer - Commit a dirty buffer
*
* ENTRY
* pbuf - pointer to BUF
* fCS - TRUE if csBuffers is held, FALSE if not
*
* EXIT
* ERROR_SUCCESS (0) if successful, non-zero if not.
*
* NOTES
* Since we clear BUF_DIRTY before calling WriteVolume, it's important
* we do something to prevent FindBuffer from getting any bright ideas
* about reusing it before we have finished writing the original dirty
* contents. Furthermore, since we don't want to hold csBuffers across
* the potentially lengthy WriteVolume request, applying another hold to
* the buffer is really our only option.
*/
DWORD CommitBuffer(PBUF pbuf, BOOL fCS)
{
PBYTE pbMod = NULL;
int cbMod = 0;
DWORD dwError = ERROR_SUCCESS;
PVOLUME pvol = pbuf->b_pvol;
BOOL fWriteThrough = FALSE;
#ifdef UNDER_CE ASSERT(fCS == OWNCRITICALSECTION(&pvol->v_csBuffers));
#endif
// If buffer is valid and dirty...
if (!(pbuf->b_flags & BUF_INVALID) && (pbuf->b_flags & BUF_DIRTY)) {
if (pvol->v_flags & VOLF_READONLY) {
dwError = ERROR_WRITE_PROTECT;
}
DEBUGMSG(ZONE_BUFFERS,(DBGTEXT("FATFS!CommitBuffer(block %d)\r\n"), pbuf->b_blk));
// Hold the buffer to insure that FindBuffer won't try to steal
// it while the DIRTY bit is clear yet the buffer is still dirty.
HoldBuffer(pbuf);
// We clear the DIRTY bit before writing the buffer out, to
// insure that we don't inadvertently lose some other thread's
// modification to the buffer before our WriteVolume call is
// complete.
CleanBuffer(pbuf);
// If the caller tells us that csBuffers is currently held, release
// it across the WriteVolume call.
pbuf->b_flags |= BUF_BUSY;
// We used to release this critical section across a disk write but it would
// sometimes cause data corruption on multithreaded writes with odd buffer
// sizes. Beware if you try to "optimize" by adding the release back in.
//if (fCS)
// LeaveCriticalSection(&csBuffers);
fWriteThrough = (pbuf->b_pstm && (pbuf->b_pstm->s_flags & STF_WRITETHRU));
dwError = WriteVolume(pvol, pbuf->b_blk, 1, pbuf->b_pdata, fWriteThrough);
pbuf->b_flags &= ~BUF_BUSY;
if (dwError) {
if (dwError == ERROR_WRITE_PROTECT) {
// Invalidate the buffer, it can't be written and is presumably
// now out of sync with the disk's actual contents.
pbuf->b_flags |= BUF_INVALID;
}
else {
// If the cause of failure might simply be that the disk has been
// yanked, we can always hope that the user will put the disk back
// in, so let's just re-dirty the buffer.
DirtyBufferError(pbuf, pbMod, cbMod);
}
}
UnholdBuffer(pbuf);
}
return dwError;
}
/* CleanBuffer
*
* ENTRY
* pbuf - pointer to BUF
*
* EXIT
* The buffer is marked CLEAN, if it wasn't already.
*/
// 根据实际的标记位进行清除buffer的操作,一次只能操作一个buffer
void CleanBuffer(PBUF pbuf)
{
PVOLUME pvol = pbuf->b_pvol;
ASSERTHELDBUFFER(pbuf);
// 清除buffer的标记位,即标记为没有有效数据
pbuf->b_flags &= ~BUF_DIRTY;
if (pbuf->b_flags & BUF_ERROR) {
EnterCriticalSection(&pvol->v_csBuffers);
if (pbuf->b_flags & BUF_ERROR) {
//更新标记位并减少pvol->v_cbufError中记录的错误个数
pvol->v_cbufError--;
//
if (pvol->v_cbufError % MIN_BUFFERS == 0)
InterlockedIncrement(&pvol->v_cBufThreads);
pbuf->b_flags &= ~BUF_ERROR;
}
LeaveCriticalSection(&pvol->v_csBuffers);
}
}
/* FindBuffer
*
* ENTRY
* pvol - pointer to VOLUME
* blk - desired block on VOLUME
* pstm - pointer to DSTREAM containing block
* fNoRead - TRUE to skip reading the coresponding disk data
* ppbuf - pointer to address of BUF (to be returned)
*
* EXIT
* ERROR_SUCCESS (0) if successful, non-zero if not.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -