reactos/drivers/filesystems/udfs/Include/wcache_lib.cpp
Ronny Borchert 9d33a2056e
[NTFS][UDFS] Fix build with NDEBUG undefined (#4171)
- Fix DumpBTreeNode() parameters in ntfs/btree.c
- Comment out unused variable in env_spec.cpp
- Fix missed brackets in else clause in wcache_lib.cpp
2021-12-20 21:19:53 +01:00

3654 lines
124 KiB
C++

////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////
/*********************************************************************/
OSSTATUS __fastcall WCacheCheckLimits(IN PW_CACHE Cache,
IN PVOID Context,
IN lba_t ReqLba,
IN ULONG BCount);
OSSTATUS __fastcall WCacheCheckLimitsRAM(IN PW_CACHE Cache,
IN PVOID Context,
IN lba_t ReqLba,
IN ULONG BCount);
OSSTATUS __fastcall WCacheCheckLimitsRW(IN PW_CACHE Cache,
IN PVOID Context,
IN lba_t ReqLba,
IN ULONG BCount);
OSSTATUS __fastcall WCacheCheckLimitsR(IN PW_CACHE Cache,
IN PVOID Context,
IN lba_t ReqLba,
IN ULONG BCount);
VOID __fastcall WCachePurgeAllRW(IN PW_CACHE Cache,
IN PVOID Context);
VOID __fastcall WCacheFlushAllRW(IN PW_CACHE Cache,
IN PVOID Context);
VOID __fastcall WCachePurgeAllR(IN PW_CACHE Cache,
IN PVOID Context);
OSSTATUS __fastcall WCacheDecodeFlags(IN PW_CACHE Cache,
IN ULONG Flags);
#define ASYNC_STATE_NONE 0
#define ASYNC_STATE_READ_PRE 1
#define ASYNC_STATE_READ 2
#define ASYNC_STATE_WRITE_PRE 3
#define ASYNC_STATE_WRITE 4
#define ASYNC_STATE_DONE 5
#define ASYNC_CMD_NONE 0
#define ASYNC_CMD_READ 1
#define ASYNC_CMD_UPDATE 2
#define WCACHE_MAX_CHAIN (0x10)
#define MEM_WCCTX_TAG 'xtCW'
#define MEM_WCFRM_TAG 'rfCW'
#define MEM_WCBUF_TAG 'fbCW'
#define USE_WC_PRINT
#ifdef USE_WC_PRINT
#define WcPrint UDFPrint
#else
#define WcPrint(x) {;}
#endif
typedef struct _W_CACHE_ASYNC {
UDF_PH_CALL_CONTEXT PhContext;
ULONG State;
ULONG Cmd;
PW_CACHE Cache;
PVOID Buffer;
PVOID Buffer2;
SIZE_T TransferredBytes;
ULONG BCount;
lba_t Lba;
struct _W_CACHE_ASYNC* NextWContext;
struct _W_CACHE_ASYNC* PrevWContext;
} W_CACHE_ASYNC, *PW_CACHE_ASYNC;
VOID
WCacheUpdatePacketComplete(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to tail async IO context
IN BOOLEAN FreePacket = TRUE
);
/*********************************************************************/
ULONG WCache_random;
/*
WCacheInit__() fills all necesary fileds in passed in PW_CACHE Cache
structure, allocates memory and synchronization resources.
Cacheable area is subdiveded on Frames - contiguous sets of blocks.
Internally each Frame is an array of pointers and attributes of cached
Blocks. To optimize memory usage WCache keeps in memory limited number
of frames (MaxFrames).
Frame length (number of Blocks) must be be a power of 2 and aligned on
minimum writeable block size - Packet.
Packet size must be a power of 2 (2, 4, 8, 16, etc.).
Each cached Block belongs to one of the Frames. To optimize memory usage
WCache keeps in memory limited number of Blocks (MaxBlocks). Block size
must be a power of 2.
WCache splits low-level request(s) into some parts if requested data length
exceeds MaxBytesToRead.
If requested data length exceeds maximum cache size WCache makes
recursive calls to read/write routines with shorter requests
WCacheInit__() returns initialization status. If initialization failed,
all allocated memory and resources are automaticelly freed.
Public routine
*/
OSSTATUS
WCacheInit__(
IN PW_CACHE Cache, // pointer to the Cache Control structure to be initialized
IN ULONG MaxFrames, // maximum number of Frames to be kept in memory
// simultaneously
IN ULONG MaxBlocks, // maximum number of Blocks to be kept in memory
// simultaneously
IN SIZE_T MaxBytesToRead, // maximum IO length (split boundary)
IN ULONG PacketSizeSh, // number of blocks in packet (bit shift)
// Packes size = 2^PacketSizeSh
IN ULONG BlockSizeSh, // Block size (bit shift)
// Block size = 2^BlockSizeSh
IN ULONG BlocksPerFrameSh,// number of blocks in Frame (bit shift)
// Frame size = 2^BlocksPerFrameSh
IN lba_t FirstLba, // Logical Block Address (LBA) of the 1st block
// in cacheable area
IN lba_t LastLba, // Logical Block Address (LBA) of the last block
// in cacheable area
IN ULONG Mode, // media mode:
// WCACHE_MODE_ROM
// WCACHE_MODE_RW
// WCACHE_MODE_R
// WCACHE_MODE_RAM
// the following modes are planned to be implemented:
// WCACHE_MODE_EWR
IN ULONG Flags, // cache mode flags:
// WCACHE_CACHE_WHOLE_PACKET
// read long (Packet-sized) blocks of
// data from media
IN ULONG FramesToKeepFree,
// number of Frames to be flushed & purged from cache
// when Frame counter reaches top-limit and allocation
// of a new Frame required
IN PWRITE_BLOCK WriteProc,
// pointer to synchronous physical write call-back routine
IN PREAD_BLOCK ReadProc,
// pointer to synchronous physical read call-back routine
IN PWRITE_BLOCK_ASYNC WriteProcAsync,
// pointer to _asynchronous_ physical write call-back routine
// currently must be set to NULL because async support
// is not completly implemented
IN PREAD_BLOCK_ASYNC ReadProcAsync,
// pointer to _asynchronous_ physical read call-back routine
// must be set to NULL (see above)
IN PCHECK_BLOCK CheckUsedProc,
// pointer to call-back routine that checks whether the Block
// specified (by LBA) is allocated for some data or should
// be treated as unused (and thus, zero-filled).
// Is used to avoid physical reads and writes from/to such Blocks
IN PUPDATE_RELOC UpdateRelocProc,
// pointer to call-back routine that updates caller's
// relocation table _after_ physical write (append) in WORM
// (WCACHE_MODE_R) mode. WCache sends original and new
// (derived from last LBA) logical addresses to this routine
IN PWC_ERROR_HANDLER ErrorHandlerProc
)
{
ULONG l1, l2, l3;
ULONG PacketSize = (1) << PacketSizeSh;
ULONG BlockSize = (1) << BlockSizeSh;
ULONG BlocksPerFrame = (1) << BlocksPerFrameSh;
OSSTATUS RC = STATUS_SUCCESS;
LARGE_INTEGER rseed;
ULONG res_init_flags = 0;
#define WCLOCK_RES 1
_SEH2_TRY {
// check input parameters
if(Mode == WCACHE_MODE_R) {
UDFPrint(("Disable Async-Write for WORM media\n"));
WriteProcAsync = NULL;
}
if((MaxBlocks % PacketSize) || !MaxBlocks) {
UDFPrint(("Total number of sectors must be packet-size-aligned\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
if(BlocksPerFrame % PacketSize) {
UDFPrint(("Number of sectors per Frame must be packet-size-aligned\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
if(!ReadProc) {
UDFPrint(("Read routine pointer must be valid\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
if(FirstLba >= LastLba) {
UDFPrint(("Invalid cached area parameters: (%x - %x)\n",FirstLba, LastLba));
try_return(RC = STATUS_INVALID_PARAMETER);
}
if(!MaxFrames) {
UDFPrint(("Total frame number must be non-zero\n",FirstLba, LastLba));
try_return(RC = STATUS_INVALID_PARAMETER);
}
if(Mode > WCACHE_MODE_MAX) {
UDFPrint(("Invalid media mode. Should be 0-%x\n",WCACHE_MODE_MAX));
try_return(RC = STATUS_INVALID_PARAMETER);
}
if(FramesToKeepFree >= MaxFrames/2) {
UDFPrint(("Invalid FramesToKeepFree (%x). Should be Less or equal to MaxFrames/2 (%x)\n", FramesToKeepFree, MaxFrames/2));
try_return(RC = STATUS_INVALID_PARAMETER);
}
// check 'features'
if(!WriteProc) {
UDFPrint(("Write routine not specified\n"));
UDFPrint(("Read-only mode enabled\n"));
}
MaxBlocks = max(MaxBlocks, BlocksPerFrame*3);
// initialize required structures
// we'll align structure size on system page size to
// avoid system crashes caused by pool fragmentation
if(!(Cache->FrameList =
(PW_CACHE_FRAME)MyAllocatePoolTag__(NonPagedPool, l1 = (((LastLba >> BlocksPerFrameSh)+1)*sizeof(W_CACHE_FRAME)), MEM_WCFRM_TAG) )) {
UDFPrint(("Cache init err 1\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
if(!(Cache->CachedBlocksList =
(PULONG)MyAllocatePoolTag__(NonPagedPool, l2 = ((MaxBlocks+2)*sizeof(lba_t)), MEM_WCFRM_TAG) )) {
UDFPrint(("Cache init err 2\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
if(!(Cache->CachedModifiedBlocksList =
(PULONG)MyAllocatePoolTag__(NonPagedPool, l2, MEM_WCFRM_TAG) )) {
UDFPrint(("Cache init err 3\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
if(!(Cache->CachedFramesList =
(PULONG)MyAllocatePoolTag__(NonPagedPool, l3 = ((MaxFrames+2)*sizeof(lba_t)), MEM_WCFRM_TAG) )) {
UDFPrint(("Cache init err 4\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
RtlZeroMemory(Cache->FrameList, l1);
RtlZeroMemory(Cache->CachedBlocksList, l2);
RtlZeroMemory(Cache->CachedModifiedBlocksList, l2);
RtlZeroMemory(Cache->CachedFramesList, l3);
// remember all useful parameters
Cache->BlocksPerFrame = BlocksPerFrame;
Cache->BlocksPerFrameSh = BlocksPerFrameSh;
Cache->BlockCount = 0;
Cache->MaxBlocks = MaxBlocks;
Cache->MaxBytesToRead = MaxBytesToRead;
Cache->FrameCount = 0;
Cache->MaxFrames = MaxFrames;
Cache->PacketSize = PacketSize;
Cache->PacketSizeSh = PacketSizeSh;
Cache->BlockSize = BlockSize;
Cache->BlockSizeSh = BlockSizeSh;
Cache->WriteCount = 0;
Cache->FirstLba = FirstLba;
Cache->LastLba = LastLba;
Cache->Mode = Mode;
if(!OS_SUCCESS(RC = WCacheDecodeFlags(Cache, Flags))) {
return RC;
}
Cache->FramesToKeepFree = FramesToKeepFree;
Cache->WriteProc = WriteProc;
Cache->ReadProc = ReadProc;
Cache->WriteProcAsync = WriteProcAsync;
Cache->ReadProcAsync = ReadProcAsync;
Cache->CheckUsedProc = CheckUsedProc;
Cache->UpdateRelocProc = UpdateRelocProc;
Cache->ErrorHandlerProc = ErrorHandlerProc;
// init permanent tmp buffers
if(!(Cache->tmp_buff =
(PCHAR)MyAllocatePoolTag__(NonPagedPool, PacketSize*BlockSize, MEM_WCFRM_TAG))) {
UDFPrint(("Cache init err 5.W\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
if(!(Cache->tmp_buff_r =
(PCHAR)MyAllocatePoolTag__(NonPagedPool, PacketSize*BlockSize, MEM_WCFRM_TAG))) {
UDFPrint(("Cache init err 5.R\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
if(!(Cache->reloc_tab =
(PULONG)MyAllocatePoolTag__(NonPagedPool, Cache->PacketSize*sizeof(ULONG), MEM_WCFRM_TAG))) {
UDFPrint(("Cache init err 6\n"));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
if(!OS_SUCCESS(RC = ExInitializeResourceLite(&(Cache->WCacheLock)))) {
UDFPrint(("Cache init err (res)\n"));
try_return(RC);
}
res_init_flags |= WCLOCK_RES;
KeQuerySystemTime((PLARGE_INTEGER)(&rseed));
WCache_random = rseed.LowPart;
try_exit: NOTHING;
} _SEH2_FINALLY {
if(!OS_SUCCESS(RC)) {
if(res_init_flags & WCLOCK_RES)
ExDeleteResourceLite(&(Cache->WCacheLock));
if(Cache->FrameList)
MyFreePool__(Cache->FrameList);
if(Cache->CachedBlocksList)
MyFreePool__(Cache->CachedBlocksList);
if(Cache->CachedModifiedBlocksList)
MyFreePool__(Cache->CachedModifiedBlocksList);
if(Cache->CachedFramesList)
MyFreePool__(Cache->CachedFramesList);
if(Cache->tmp_buff_r)
MyFreePool__(Cache->tmp_buff_r);
if(Cache->tmp_buff)
MyFreePool__(Cache->tmp_buff);
if(Cache->reloc_tab)
MyFreePool__(Cache->reloc_tab);
RtlZeroMemory(Cache, sizeof(W_CACHE));
} else {
Cache->Tag = 0xCAC11E00;
}
} _SEH2_END;
return RC;
} // end WCacheInit__()
/*
WCacheRandom() - just a random generator
Returns random LONGLONG number
Internal routine
*/
LONGLONG
WCacheRandom(VOID)
{
WCache_random = (WCache_random * 0x8088405 + 1);
return WCache_random;
} // end WCacheRandom()
/*
WCacheFindLbaToRelease() finds Block to be flushed and purged from cache
Returns random LBA
Internal routine
*/
lba_t
__fastcall
WCacheFindLbaToRelease(
IN PW_CACHE Cache
)
{
if(!(Cache->BlockCount))
return WCACHE_INVALID_LBA;
return(Cache->CachedBlocksList[((ULONG)WCacheRandom() % Cache->BlockCount)]);
} // end WCacheFindLbaToRelease()
/*
WCacheFindModifiedLbaToRelease() finds Block to be flushed and purged from cache.
This routine looks for Blocks among modified ones
Returns random LBA (nodified)
Internal routine
*/
lba_t
__fastcall
WCacheFindModifiedLbaToRelease(
IN PW_CACHE Cache
)
{
if(!(Cache->WriteCount))
return WCACHE_INVALID_LBA;
return(Cache->CachedModifiedBlocksList[((ULONG)WCacheRandom() % Cache->WriteCount)]);
} // end WCacheFindModifiedLbaToRelease()
/*
WCacheFindFrameToRelease() finds Frame to be flushed and purged with all
Blocks (from this Frame) from cache
Returns random Frame number
Internal routine
*/
lba_t
__fastcall
WCacheFindFrameToRelease(
IN PW_CACHE Cache
)
{
ULONG i, j;
ULONG frame = 0;
ULONG prev_uc = -1;
ULONG uc = -1;
lba_t lba;
BOOLEAN mod = FALSE;
if(!(Cache->FrameCount))
return 0;
/*
return(Cache->CachedFramesList[((ULONG)WCacheRandom() % Cache->FrameCount)]);
*/
for(i=0; i<Cache->FrameCount; i++) {
j = Cache->CachedFramesList[i];
mod |= (Cache->FrameList[j].UpdateCount != 0);
uc = Cache->FrameList[j].UpdateCount*32 + Cache->FrameList[j].AccessCount;
if(prev_uc > uc) {
prev_uc = uc;
frame = j;
}
}
if(!mod) {
frame = Cache->CachedFramesList[((ULONG)WCacheRandom() % Cache->FrameCount)];
lba = frame << Cache->BlocksPerFrameSh;
WcPrint(("WC:-frm %x\n", lba));
} else {
lba = frame << Cache->BlocksPerFrameSh;
WcPrint(("WC:-frm(mod) %x\n", lba));
for(i=0; i<Cache->FrameCount; i++) {
j = Cache->CachedFramesList[i];
Cache->FrameList[j].UpdateCount = (Cache->FrameList[j].UpdateCount*2)/3;
Cache->FrameList[j].AccessCount = (Cache->FrameList[j].AccessCount*3)/4;
}
}
return frame;
} // end WCacheFindFrameToRelease()
/*
WCacheGetSortedListIndex() returns index of searched Lba
(Lba is ULONG in sorted array) or index of minimal cached Lba
greater than searched.
If requested Lba is less than minimum cached, 0 is returned.
If requested Lba is greater than maximum cached, BlockCount value
is returned.
Internal routine
*/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4035) // re-enable below
#endif
ULONG
//__fastcall
WCacheGetSortedListIndex(
IN ULONG BlockCount, // number of items in array (pointed by List)
IN lba_t* List, // pointer to sorted (ASC) array of ULONGs
IN lba_t Lba // ULONG value to be searched for
)
{
if(!BlockCount)
return 0;
#if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__)
__asm push ecx
__asm push ebx
__asm push edx
__asm push esi
__asm push edi
// left = 0;
// right = BlockCount - 1;
// pos = 0;
__asm xor edx,edx // left
__asm mov ebx,BlockCount
__asm dec ebx // right
__asm xor esi,esi // pos
__asm mov edi,List // List
__asm mov ecx,Lba // Lba
While_1:
// while(left != right) {
__asm cmp edx,ebx
__asm jz EO_while_1
// pos = (left + right) >> 1;
__asm lea esi,[ebx+edx]
__asm shr esi,1
// if(List[pos] == Lba)
// return pos;
__asm mov eax,[edi+esi*4]
__asm cmp eax,ecx
__asm jz EO_while_2
// if(right - left == 1) {
__asm sub ebx,edx
__asm cmp ebx,1
__asm jne NO_r_sub_l_eq_1
// if(List[pos+1] < Lba) <=> if(List[pos+1] >= Lba)
// return (pos+2); <=> break;
// break; <=> return (pos+2);
__asm cmp [edi+esi*4+4],ecx
__asm jae EO_while_1
__asm add esi,2
__asm jmp EO_while_2
// }
NO_r_sub_l_eq_1:
// if(List[pos] < Lba) {
__asm cmp eax,ecx
__asm jae Update_r
// left = pos;
__asm add ebx,edx
__asm mov edx,esi
__asm jmp While_1
// } else {
Update_r:
// right = pos;
__asm mov ebx,esi
__asm jmp While_1
// }
// }
EO_while_1:
// if((List[pos] < Lba) && ((pos+1) <= BlockCount)) pos++;
__asm mov eax,[edi+esi*4]
__asm cmp eax,ecx
__asm jae EO_while_2
__asm inc esi
__asm cmp esi,BlockCount
__asm jbe EO_while_2
__asm dec esi
EO_while_2:
// return pos;
__asm mov eax,esi
__asm pop edi
__asm pop esi
__asm pop edx
__asm pop ebx
__asm pop ecx
#else // NO X86 optimization , use generic C/C++
ULONG pos;
ULONG left;
ULONG right;
if(!BlockCount)
return 0;
left = 0;
right = BlockCount - 1;
pos = 0;
while(left != right) {
pos = (left + right) >> 1;
if(List[pos] == Lba)
return pos;
if(right - left == 1) {
if(List[pos+1] < Lba)
return (pos+2);
break;
}
if(List[pos] < Lba) {
left = pos;
} else {
right = pos;
}
}
if((List[pos] < Lba) && ((pos+1) <= BlockCount)) pos++;
return pos;
#endif // _X86_
}
#ifdef _MSC_VER
#pragma warning(pop) // re-enable warning #4035
#endif
/*
WCacheInsertRangeToList() inserts values laying in range described
by Lba (1st value) and BCount (number of sequentially incremented
values) in sorted array of ULONGs pointed by List.
Ex.: (Lba, BCount)=(7,3) will insert values {7,8,9}.
If target array already contains one or more values falling in
requested range, they will be removed before insertion.
WCacheInsertRangeToList() updates value of (*BlockCount) to reflect
performed changes.
WCacheInsertRangeToList() assumes that target array is of enough size.
Internal routine
*/
VOID
__fastcall
WCacheInsertRangeToList(
IN lba_t* List, // pointer to sorted (ASC) array of ULONGs
IN PULONG BlockCount, // pointer to number of items in array (pointed by List)
IN lba_t Lba, // initial value for insertion
IN ULONG BCount // number of sequentially incremented values to be inserted
)
{
if(!BCount)
return;
ASSERT(!(BCount & 0x80000000));
ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba);
ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+BCount);
ULONG offs = firstPos + BCount - lastPos;
if(offs) {
// move list tail
// ASSERT(lastPos+offs + ((*BlockCount) - lastPos) <= qq);
if(*BlockCount) {
#ifdef WCACHE_BOUND_CHECKS
MyCheckArray(List, lastPos+offs+(*BlockCount)-lastPos-1);
#endif //WCACHE_BOUND_CHECKS
DbgMoveMemory(&(List[lastPos+offs]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG));
}
lastPos += offs;
for(; firstPos<lastPos; firstPos++) {
#ifdef WCACHE_BOUND_CHECKS
MyCheckArray(List, firstPos);
#endif //WCACHE_BOUND_CHECKS
List[firstPos] = Lba;
Lba++;
}
(*BlockCount) += offs;
}
} // end WCacheInsertRangeToList()
/*
WCacheInsertItemToList() inserts value Lba in sorted array of
ULONGs pointed by List.
If target array already contains requested value, no
operations are performed.
WCacheInsertItemToList() updates value of (*BlockCount) to reflect
performed changes.
WCacheInsertItemToList() assumes that target array is of enough size.
Internal routine
*/
VOID
__fastcall
WCacheInsertItemToList(
IN lba_t* List, // pointer to sorted (ASC) array of lba_t's
IN PULONG BlockCount, // pointer to number of items in array (pointed by List)
IN lba_t Lba // value to be inserted
)
{
ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+1);
if(firstPos && (List[firstPos-1] == Lba))
return;
// move list tail
if(*BlockCount) {
#ifdef WCACHE_BOUND_CHECKS
MyCheckArray(List, firstPos+1+(*BlockCount)-firstPos-1);
#endif //WCACHE_BOUND_CHECKS
// DbgMoveMemory(&(List[firstPos+1]), &(List[firstPos]), ((*BlockCount) - firstPos)*sizeof(ULONG));
DbgMoveMemory(&(List[firstPos+1]), &(List[firstPos]), ((*BlockCount) - firstPos) * sizeof(ULONG));
}
#ifdef WCACHE_BOUND_CHECKS
MyCheckArray(List, firstPos);
#endif //WCACHE_BOUND_CHECKS
List[firstPos] = Lba;
(*BlockCount) ++;
} // end WCacheInsertItemToList()
/*
WCacheRemoveRangeFromList() removes values falling in range described
by Lba (1st value) and BCount (number of sequentially incremented
values) from sorted array of ULONGs pointed by List.
Ex.: (Lba, BCount)=(7,3) will remove values {7,8,9}.
If target array doesn't contain values falling in
requested range, no operation is performed.
WCacheRemoveRangeFromList() updates value of (*BlockCount) to reflect
performed changes.
Internal routine
*/
VOID
__fastcall
WCacheRemoveRangeFromList(
IN lba_t* List, // pointer to sorted (ASC) array of ULONGs
IN PULONG BlockCount, // pointer to number of items in array (pointed by List)
IN lba_t Lba, // initial value for removal
IN ULONG BCount // number of sequentially incremented values to be removed
)
{
ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba);
ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+BCount);
ULONG offs = lastPos - firstPos;
if(offs) {
// move list tail
DbgMoveMemory(&(List[lastPos-offs]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG));
(*BlockCount) -= offs;
}
} // end WCacheRemoveRangeFromList()
/*
WCacheRemoveItemFromList() removes value Lba from sorted array
of ULONGs pointed by List.
If target array doesn't contain requested value, no
operations are performed.
WCacheRemoveItemFromList() updates value of (*BlockCount) to reflect
performed changes.
Internal routine
*/
VOID
__fastcall
WCacheRemoveItemFromList(
IN lba_t* List, // pointer to sorted (ASC) array of ULONGs
IN PULONG BlockCount, // pointer to number of items in array (pointed by List)
IN lba_t Lba // value to be removed
)
{
if(!(*BlockCount)) return;
ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+1);
if(!lastPos || (lastPos && (List[lastPos-1] != Lba)))
return;
// move list tail
DbgMoveMemory(&(List[lastPos-1]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG));
(*BlockCount) --;
} // end WCacheRemoveItemFromList()
/*
WCacheInitFrame() allocates storage for Frame (block_array)
with index 'frame', fills it with 0 (none of Blocks from
this Frame is cached) and inserts it's index to sorted array
of frame indexes.
WCacheInitFrame() also checks if number of frames reaches limit
and invokes WCacheCheckLimits() to free some Frames/Blocks
Internal routine
*/
PW_CACHE_ENTRY
__fastcall
WCacheInitFrame(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // caller's context (currently unused)
IN ULONG frame // frame index
)
{
PW_CACHE_ENTRY block_array;
ULONG l;
#ifdef DBG
ULONG old_count = Cache->FrameCount;
#endif //DBG
// We are about to add new cache frame.
// Thus check if we have enough free entries and
// flush unused ones if it is neccessary.
if(Cache->FrameCount >= Cache->MaxFrames) {
BrutePoint();
WCacheCheckLimits(Cache, Context, frame << Cache->BlocksPerFrameSh, Cache->PacketSize*2);
}
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = (PW_CACHE_ENTRY)MyAllocatePoolTag__(NonPagedPool, l = sizeof(W_CACHE_ENTRY) << Cache->BlocksPerFrameSh, MEM_WCFRM_TAG);
Cache->FrameList[frame].Frame = block_array;
// Keep history !!!
//Cache->FrameList[frame].UpdateCount = 0;
//Cache->FrameList[frame].AccessCount = 0;
if(block_array) {
ASSERT((ULONG_PTR)block_array > 0x1000);
WCacheInsertItemToList(Cache->CachedFramesList, &(Cache->FrameCount), frame);
RtlZeroMemory(block_array, l);
} else {
BrutePoint();
}
ASSERT(Cache->FrameCount <= Cache->MaxFrames);
#ifdef DBG
ASSERT(old_count < Cache->FrameCount);
#endif //DBG
return block_array;
} // end WCacheInitFrame()
/*
WCacheRemoveFrame() frees storage for Frame (block_array) with
index 'frame' and removes it's index from sorted array of
frame indexes.
Internal routine
*/
VOID
__fastcall
WCacheRemoveFrame(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user's context (currently unused)
IN ULONG frame // frame index
)
{
PW_CACHE_ENTRY block_array;
#ifdef DBG
ULONG old_count = Cache->FrameCount;
#endif //DBG
ASSERT(Cache->FrameCount <= Cache->MaxFrames);
block_array = Cache->FrameList[frame].Frame;
WCacheRemoveItemFromList(Cache->CachedFramesList, &(Cache->FrameCount), frame);
MyFreePool__(block_array);
// ASSERT(!(Cache->FrameList[frame].WriteCount));
// ASSERT(!(Cache->FrameList[frame].WriteCount));
Cache->FrameList[frame].Frame = NULL;
ASSERT(Cache->FrameCount < Cache->MaxFrames);
#ifdef DBG
ASSERT(old_count > Cache->FrameCount);
#endif //DBG
} // end WCacheRemoveFrame()
/*
WCacheSetModFlag() sets Modified flag for Block with offset 'i'
in Frame 'block_array'
Internal routine
*/
#define WCacheSetModFlag(block_array, i) \
*((PULONG)&(block_array[i].Sector)) |= WCACHE_FLAG_MODIFIED
/*
WCacheClrModFlag() clears Modified flag for Block with offset 'i'
in Frame 'block_array'
Internal routine
*/
#define WCacheClrModFlag(block_array, i) \
*((PULONG)&(block_array[i].Sector)) &= ~WCACHE_FLAG_MODIFIED
/*
WCacheGetModFlag() returns non-zero value if Modified flag for
Block with offset 'i' in Frame 'block_array' is set. Otherwise
0 is returned.
Internal routine
*/
#define WCacheGetModFlag(block_array, i) \
(*((PULONG)&(block_array[i].Sector)) & WCACHE_FLAG_MODIFIED)
#if 0
/*
WCacheSetBadFlag() sets Modified flag for Block with offset 'i'
in Frame 'block_array'
Internal routine
*/
#define WCacheSetBadFlag(block_array, i) \
*((PULONG)&(block_array[i].Sector)) |= WCACHE_FLAG_BAD
/*
WCacheClrBadFlag() clears Modified flag for Block with offset 'i'
in Frame 'block_array'
Internal routine
*/
#define WCacheClrBadFlag(block_array, i) \
*((PULONG)&(block_array[i].Sector)) &= ~WCACHE_FLAG_BAD
/*
WCacheGetBadFlag() returns non-zero value if Modified flag for
Block with offset 'i' in Frame 'block_array' is set. Otherwise
0 is returned.
Internal routine
*/
#define WCacheGetBadFlag(block_array, i) \
(((UCHAR)(block_array[i].Sector)) & WCACHE_FLAG_BAD)
#endif //0
/*
WCacheSectorAddr() returns pointer to memory block containing cached
data for Block described by Frame (block_array) and offset in this
Frame (i). If requested Block is not cached yet NULL is returned.
Internal routine
*/
#define WCacheSectorAddr(block_array, i) \
((ULONG_PTR)(block_array[i].Sector) & WCACHE_ADDR_MASK)
/*
WCacheFreeSector() releases memory block containing cached
data for Block described by Frame (block_array) and offset in this
Frame (i). Should never be called for non-cached Blocks.
Internal routine
*/
#define WCacheFreeSector(frame, offs) \
{ \
DbgFreePool((PVOID)WCacheSectorAddr(block_array, offs)); \
block_array[offs].Sector = NULL; \
Cache->FrameList[frame].BlockCount--; \
}
/*
WCacheAllocAsyncEntry() allocates storage for async IO context,
links it to previously allocated async IO context (if any),
initializes synchronization (completion) event
and allocates temporary IO buffers.
Async IO contexts are used to create chained set of IO requests
durring top-level request precessing and wait for their completion.
Internal routine
*/
PW_CACHE_ASYNC
WCacheAllocAsyncEntry(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to the pointer to
// the head of async IO context chain
IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to the storage for pointer
// to newly allocated async IO context chain
IN ULONG BufferSize // requested IO buffer size
)
{
PW_CACHE_ASYNC WContext;
PCHAR Buffer;
WContext = (PW_CACHE_ASYNC)MyAllocatePoolTag__(NonPagedPool,sizeof(W_CACHE_ASYNC), MEM_WCCTX_TAG);
if(!WContext)
return NULL;
Buffer = (PCHAR)DbgAllocatePoolWithTag(NonPagedPool, BufferSize*(2-Cache->Chained), MEM_WCBUF_TAG);
if(!Buffer) {
MyFreePool__(WContext);
return NULL;
}
if(!Cache->Chained)
KeInitializeEvent(&(WContext->PhContext.event), SynchronizationEvent, FALSE);
WContext->Cache = Cache;
if(*PrevWContext)
(*PrevWContext)->NextWContext = WContext;
// WContext->NextWContext = (*PrevWContext);
WContext->NextWContext = NULL;
WContext->Buffer = Buffer;
WContext->Buffer2 = Buffer+(Cache->Chained ? 0 : BufferSize);
if(!(*FirstWContext))
(*FirstWContext) = WContext;
(*PrevWContext) = WContext;
return WContext;
} // end WCacheAllocAsyncEntry()
/*
WCacheFreeAsyncEntry() releases storage previously allocated for
async IO context.
Internal routine
*/
VOID
WCacheFreeAsyncEntry(
IN PW_CACHE Cache, // pointer to the Cache Control structure
PW_CACHE_ASYNC WContext // pointer to async IO context to release
)
{
DbgFreePool(WContext->Buffer);
MyFreePool__(WContext);
} // end WCacheFreeAsyncEntry()
//#define WCacheRaiseIoError(c, ct, s, l, bc, b, o, r)
OSSTATUS
WCacheRaiseIoError(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context,
IN OSSTATUS Status,
IN ULONG Lba,
IN ULONG BCount,
IN PVOID Buffer,
IN BOOLEAN ReadOp,
IN PBOOLEAN Retry
)
{
if(!Cache->ErrorHandlerProc)
return Status;
WCACHE_ERROR_CONTEXT ec;
ec.WCErrorCode = ReadOp ? WCACHE_ERROR_READ : WCACHE_ERROR_WRITE;
ec.Status = Status;
ec.ReadWrite.Lba = Lba;
ec.ReadWrite.BCount = BCount;
ec.ReadWrite.Buffer = Buffer;
Status = Cache->ErrorHandlerProc(Context, &ec);
if(Retry)
(*Retry) = ec.Retry;
return Status;
} // end WCacheRaiseIoError()
/*
WCacheUpdatePacket() attempts to updates packet containing target Block.
If async IO is enabled new IO context is added to the chain.
If packet containing target Block is modified and PrefereWrite flag
is NOT set, function returns with status STATUS_RETRY. This setting is
user in WCACHE_MODE_R mode to reduce physical writes on flush.
'State' parameter is used in async mode to determine the next processing
stege for given request
Internal routine
*/
OSSTATUS
WCacheUpdatePacket(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user's context to be passed to user-supplied
// low-level IO routines (IO callbacks)
IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to tail async IO context
IN PW_CACHE_ENTRY block_array, // pointer to target Frame
IN lba_t firstLba, // LBA of the 1st block in target Frame
IN lba_t Lba, // LBA of target Block
IN ULONG BSh, // bit shift for Block size
IN ULONG BS, // Block size (bytes)
IN ULONG PS, // Packet size (bytes)
IN ULONG PSs, // Packet size (sectors)
IN PSIZE_T ReadBytes, // pointer to number of successfully read/written bytes
IN BOOLEAN PrefereWrite, // allow physical write (flush) of modified packet
IN ULONG State // callers state
)
{
OSSTATUS status;
PCHAR tmp_buff = Cache->tmp_buff;
PCHAR tmp_buff2 = Cache->tmp_buff;
BOOLEAN mod;
BOOLEAN read;
BOOLEAN zero;
ULONG i;
lba_t Lba0;
PW_CACHE_ASYNC WContext;
BOOLEAN Async = (Cache->ReadProcAsync && Cache->WriteProcAsync);
ULONG block_type;
BOOLEAN Chained = Cache->Chained;
// Check if we are going to write down to disk
// all prewiously prepared (chained) data
if(State == ASYNC_STATE_WRITE) {
WContext = (*PrevWContext);
tmp_buff = (PCHAR)(WContext->Buffer);
tmp_buff2 = (PCHAR)(WContext->Buffer2);
if(!Chained)
mod = (DbgCompareMemory(tmp_buff2, tmp_buff, PS) != PS);
goto try_write;
}
// Check if packet contains modified blocks
// If packet contains non-cached and unchanged, but used
// blocks, it must be read from media before modification
mod = read = zero = FALSE;
Lba0 = Lba - firstLba;
for(i=0; i<PSs; i++, Lba0++) {
if(WCacheGetModFlag(block_array, Lba0)) {
mod = TRUE;
} else if(!WCacheSectorAddr(block_array,Lba0) &&
((block_type = Cache->CheckUsedProc(Context, Lba+i)) & WCACHE_BLOCK_USED) ) {
//
if(block_type & WCACHE_BLOCK_ZERO) {
zero = TRUE;
} else {
read = TRUE;
}
}
}
// check if we are allowed to write to media
if(mod && !PrefereWrite) {
return STATUS_RETRY;
}
// return STATUS_SUCCESS if requested packet contains no modified blocks
if(!mod) {
(*ReadBytes) = PS;
return STATUS_SUCCESS;
}
// pefrorm full update cycle: prepare(optional)/read/modify/write
// do some preparations
if(Chained || Async) {
// For chained and async I/O we allocates context entry
// and add it to list (chain)
// We shall only read data to temporary buffer and
// modify it. Write operations will be invoked later.
// This is introduced in order to avoid frequent
// read.write mode switching, because it significantly degrades
// performance
WContext = WCacheAllocAsyncEntry(Cache, FirstWContext, PrevWContext, PS);
if(!WContext) {
//return STATUS_INSUFFICIENT_RESOURCES;
// try to recover
Chained = FALSE;
Async = FALSE;
} else {
tmp_buff = tmp_buff2 = (PCHAR)(WContext->Buffer);
WContext->Lba = Lba;
WContext->Cmd = ASYNC_CMD_UPDATE;
WContext->State = ASYNC_STATE_NONE;
}
}
// read packet (if it necessary)
if(read) {
if(Async) {
WContext->State = ASYNC_STATE_READ;
status = Cache->ReadProcAsync(Context, WContext, tmp_buff, PS, Lba,
&(WContext->TransferredBytes));
// tmp_buff2 = (PCHAR)(WContext->Buffer2);
(*ReadBytes) = PS;
return status;
} else {
status = Cache->ReadProc(Context, tmp_buff, PS, Lba, ReadBytes, PH_TMP_BUFFER);
}
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff, WCACHE_R_OP, NULL);
if(!OS_SUCCESS(status)) {
return status;
}
}
} else
if(zero) {
RtlZeroMemory(tmp_buff, PS);
}
if(Chained) {
// indicate that we prepared for writing block to disk
WContext->State = ASYNC_STATE_WRITE_PRE;
tmp_buff2 = tmp_buff;
status = STATUS_SUCCESS;
}
// modify packet
// If we didn't read packet from media, we can't
// perform comparison to assure that packet was really modified.
// Thus, assume that it is modified in this case.
mod = !read || Cache->DoNotCompare;
Lba0 = Lba - firstLba;
for(i=0; i<PSs; i++, Lba0++) {
if( WCacheGetModFlag(block_array, Lba0) ||
(!read && WCacheSectorAddr(block_array,Lba0)) ) {
#ifdef _NTDEF_
ASSERT((ULONG)WCacheSectorAddr(block_array,Lba0) & 0x80000000);
#endif //_NTDEF_
if(!mod) {
ASSERT(read);
mod = (DbgCompareMemory(tmp_buff2 + (i << BSh),
(PVOID)WCacheSectorAddr(block_array, Lba0),
BS) != BS);
}
if(mod) {
DbgCopyMemory(tmp_buff2 + (i << BSh),
(PVOID)WCacheSectorAddr(block_array, Lba0),
BS);
}
}
}
if(Chained &&
WContext->State == ASYNC_STATE_WRITE_PRE) {
// Return if block is prepared for write and we are in chained mode.
if(!mod) {
// Mark block as written if we have found that data in it
// is not actually modified.
WContext->State = ASYNC_STATE_DONE;
(*ReadBytes) = PS;
}
return STATUS_SUCCESS;
}
// write packet
// If the check above reported some changes in packet
// we should write packet out to media.
// Otherwise, just complete request.
if(mod) {
try_write:
if(Async) {
WContext->State = ASYNC_STATE_WRITE;
status = Cache->WriteProcAsync(Context, WContext, tmp_buff2, PS, Lba,
&(WContext->TransferredBytes), FALSE);
(*ReadBytes) = PS;
} else {
status = Cache->WriteProc(Context, tmp_buff2, PS, Lba, ReadBytes, 0);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff2, WCACHE_W_OP, NULL);
}
}
} else {
if(Async)
WCacheCompleteAsync__(WContext, STATUS_SUCCESS);
(*ReadBytes) = PS;
return STATUS_SUCCESS;
}
return status;
} // end WCacheUpdatePacket()
/*
WCacheFreePacket() releases storage for all Blocks in packet.
'frame' describes Frame, offset - Block in Frame. offset should be
aligned on Packet size.
Internal routine
*/
VOID
WCacheFreePacket(
IN PW_CACHE Cache, // pointer to the Cache Control structure
// IN PVOID Context,
IN ULONG frame, // Frame index
IN PW_CACHE_ENTRY block_array, // Frame
IN ULONG offs, // offset in Frame
IN ULONG PSs // Packet size (in Blocks)
)
{
ULONG i;
// mark as non-cached & free pool
for(i=0; i<PSs; i++, offs++) {
if(WCacheSectorAddr(block_array,offs)) {
WCacheFreeSector(frame, offs);
}
}
} // end WCacheFreePacket()
/*
WCacheUpdatePacketComplete() is called to continue processing of packet
being updated.
In async mode it waits for completion of pre-read requests,
initiates writes, waits for their completion and returns control to
caller.
Internal routine
*/
VOID
WCacheUpdatePacketComplete(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to tail async IO context
IN BOOLEAN FreePacket
)
{
PW_CACHE_ASYNC WContext = (*FirstWContext);
if(!WContext)
return;
PW_CACHE_ASYNC NextWContext;
ULONG PS = Cache->BlockSize << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
ULONG frame;
lba_t firstLba;
// Walk through all chained blocks and wait
// for completion of read operations.
// Also invoke writes of already prepared packets.
while(WContext) {
if(WContext->Cmd == ASYNC_CMD_UPDATE &&
WContext->State == ASYNC_STATE_READ) {
// wait for async read for update
DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
WContext->State = ASYNC_STATE_WRITE;
WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1,
PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE);
} else
if(WContext->Cmd == ASYNC_CMD_UPDATE &&
WContext->State == ASYNC_STATE_WRITE_PRE) {
// invoke physical write it the packet is prepared for writing
// by previuous call to WCacheUpdatePacket()
WContext->State = ASYNC_STATE_WRITE;
WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1,
PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE);
WContext->State = ASYNC_STATE_DONE;
} else
if(WContext->Cmd == ASYNC_CMD_READ &&
WContext->State == ASYNC_STATE_READ) {
// wait for async read
DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
}
WContext = WContext->NextWContext;
}
// Walk through all chained blocks and wait
// and wait for completion of async writes (if any).
// Also free temporary buffers containing already written blocks.
WContext = (*FirstWContext);
while(WContext) {
NextWContext = WContext->NextWContext;
if(WContext->Cmd == ASYNC_CMD_UPDATE &&
WContext->State == ASYNC_STATE_WRITE) {
if(!Cache->Chained)
DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
frame = WContext->Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
if(FreePacket) {
WCacheFreePacket(Cache, frame,
Cache->FrameList[frame].Frame,
WContext->Lba - firstLba, PSs);
}
}
WCacheFreeAsyncEntry(Cache, WContext);
WContext = NextWContext;
}
(*FirstWContext) = NULL;
(*PrevWContext) = NULL;
} // end WCacheUpdatePacketComplete()
/*
WCacheCheckLimits() checks if we've enough free Frame- &
Block-entries under Frame- and Block-limit to feet
requested Blocks.
If there is not enough entries, WCache initiates flush & purge
process to satisfy request.
This is dispatch routine, which calls
WCacheCheckLimitsR() or WCacheCheckLimitsRW() depending on
media type.
Internal routine
*/
OSSTATUS
__fastcall
WCacheCheckLimits(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t ReqLba, // first LBA to access/cache
IN ULONG BCount // number of Blocks to access/cache
)
{
/* if(!Cache->FrameCount || !Cache->BlockCount) {
ASSERT(!Cache->FrameCount);
ASSERT(!Cache->BlockCount);
if(!Cache->FrameCount)
return STATUS_SUCCESS;
}*/
// check if we have reached Frame or Block limit
if(!Cache->FrameCount && !Cache->BlockCount) {
return STATUS_SUCCESS;
}
// check for empty frames
if(Cache->FrameCount > (Cache->MaxFrames*3)/4) {
ULONG frame;
ULONG i;
for(i=Cache->FrameCount; i>0; i--) {
frame = Cache->CachedFramesList[i-1];
// check if frame is empty
if(!(Cache->FrameList[frame].BlockCount)) {
WCacheRemoveFrame(Cache, Context, frame);
} else {
ASSERT(Cache->FrameList[frame].Frame);
}
}
}
if(!Cache->BlockCount) {
return STATUS_SUCCESS;
}
// invoke media-specific limit-checker
switch(Cache->Mode) {
case WCACHE_MODE_RAM:
return WCacheCheckLimitsRAM(Cache, Context, ReqLba, BCount);
case WCACHE_MODE_ROM:
case WCACHE_MODE_RW:
return WCacheCheckLimitsRW(Cache, Context, ReqLba, BCount);
case WCACHE_MODE_R:
return WCacheCheckLimitsR(Cache, Context, ReqLba, BCount);
}
return STATUS_DRIVER_INTERNAL_ERROR;
} // end WCacheCheckLimits()
/*
WCacheCheckLimitsRW() implements automatic flush and purge of
unused blocks to keep enough free cache entries for newly
read/written blocks for Random Access and ReWritable media
using Read/Modify/Write technology.
See also WCacheCheckLimits()
Internal routine
*/
OSSTATUS
__fastcall
WCacheCheckLimitsRW(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t ReqLba, // first LBA to access/cache
IN ULONG BCount // number of Blocks to access/cache
)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t lastLba;
lba_t Lba;
// PCHAR tmp_buff = Cache->tmp_buff;
ULONG firstPos;
ULONG lastPos;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
ULONG try_count = 0;
PW_CACHE_ENTRY block_array;
OSSTATUS status;
SIZE_T ReadBytes;
ULONG FreeFrameCount = 0;
// PVOID addr;
PW_CACHE_ASYNC FirstWContext = NULL;
PW_CACHE_ASYNC PrevWContext = NULL;
ULONG chain_count = 0;
if(Cache->FrameCount >= Cache->MaxFrames) {
FreeFrameCount = Cache->FramesToKeepFree;
} else
if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
// we need free space to grow WCache without flushing data
// for some period of time
FreeFrameCount = Cache->FramesToKeepFree;
goto Try_Another_Frame;
}
// remove(flush) some frames
while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) {
Try_Another_Frame:
if(!Cache->FrameCount || !Cache->BlockCount) {
//ASSERT(!Cache->FrameCount);
if(Cache->FrameCount) {
UDFPrint(("ASSERT: Cache->FrameCount = %d, when 0 is expected\n", Cache->FrameCount));
}
ASSERT(!Cache->BlockCount);
if(!Cache->FrameCount)
break;
}
frame = WCacheFindFrameToRelease(Cache);
#if 0
if(Cache->FrameList[frame].WriteCount) {
try_count++;
if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame;
} else {
try_count = 0;
}
#else
if(Cache->FrameList[frame].UpdateCount) {
try_count = MAX_TRIES_FOR_NA;
} else {
try_count = 0;
}
#endif
if(FreeFrameCount)
FreeFrameCount--;
firstLba = frame << Cache->BlocksPerFrameSh;
lastLba = firstLba + Cache->BlocksPerFrame;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
UDFPrint(("Hmm...\n"));
BrutePoint();
return STATUS_DRIVER_INTERNAL_ERROR;
}
while(firstPos < lastPos) {
// flush packet
Lba = List[firstPos] & ~(PSs-1);
// write packet out or prepare and add to chain (if chained mode enabled)
status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
if(status != STATUS_PENDING) {
// free memory
WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
}
Lba += PSs;
while((firstPos < lastPos) && (Lba > List[firstPos])) {
firstPos++;
}
chain_count++;
// write chained packets
if(chain_count >= WCACHE_MAX_CHAIN) {
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
chain_count = 0;
}
}
// remove flushed blocks from all lists
WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
WCacheRemoveFrame(Cache, Context, frame);
}
// check if we try to read too much data
if(BCount > Cache->MaxBlocks) {
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
return STATUS_INVALID_PARAMETER;
}
// remove(flush) packet
while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
try_count = 0;
Try_Another_Block:
Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1);
if(Lba == WCACHE_INVALID_LBA) {
ASSERT(!Cache->FrameCount);
ASSERT(!Cache->BlockCount);
break;
}
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
// write already prepared blocks to disk and return error
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
ASSERT(FALSE);
return STATUS_DRIVER_INTERNAL_ERROR;
}
// write packet out or prepare and add to chain (if chained mode enabled)
status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
Lba, BSh, BS, PS, PSs, &ReadBytes, (try_count >= MAX_TRIES_FOR_NA), ASYNC_STATE_NONE);
if(status == STATUS_RETRY) {
try_count++;
goto Try_Another_Block;
}
// free memory
WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
// check if frame is empty
if(!(Cache->FrameList[frame].BlockCount)) {
WCacheRemoveFrame(Cache, Context, frame);
} else {
ASSERT(Cache->FrameList[frame].Frame);
}
chain_count++;
if(chain_count >= WCACHE_MAX_CHAIN) {
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
chain_count = 0;
}
}
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
return STATUS_SUCCESS;
} // end WCacheCheckLimitsRW()
OSSTATUS
__fastcall
WCacheFlushBlocksRAM(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
PW_CACHE_ENTRY block_array,
lba_t* List,
ULONG firstPos,
ULONG lastPos,
BOOLEAN Purge
)
{
ULONG frame;
lba_t Lba;
lba_t PrevLba;
lba_t firstLba;
PCHAR tmp_buff = NULL;
ULONG n;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
// ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
SIZE_T _WrittenBytes;
OSSTATUS status = STATUS_SUCCESS;
frame = List[firstPos] >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
while(firstPos < lastPos) {
// flush blocks
ASSERT(Cache->FrameCount <= Cache->MaxFrames);
Lba = List[firstPos];
if(!WCacheGetModFlag(block_array, Lba - firstLba)) {
// free memory
if(Purge) {
WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, 1);
}
firstPos++;
continue;
}
tmp_buff = Cache->tmp_buff;
PrevLba = Lba;
n=1;
while((firstPos+n < lastPos) &&
(List[firstPos+n] == PrevLba+1)) {
PrevLba++;
if(!WCacheGetModFlag(block_array, PrevLba - firstLba))
break;
DbgCopyMemory(tmp_buff + (n << BSh),
(PVOID)WCacheSectorAddr(block_array, PrevLba - firstLba),
BS);
n++;
if(n >= PSs)
break;
}
if(n > 1) {
DbgCopyMemory(tmp_buff,
(PVOID)WCacheSectorAddr(block_array, Lba - firstLba),
BS);
} else {
tmp_buff = (PCHAR)WCacheSectorAddr(block_array, Lba - firstLba);
}
// write sectors out
status = Cache->WriteProc(Context, tmp_buff, n<<BSh, Lba, &_WrittenBytes, 0);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, n, tmp_buff, WCACHE_W_OP, NULL);
if(!OS_SUCCESS(status)) {
BrutePoint();
}
}
firstPos += n;
if(Purge) {
// free memory
WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, n);
} else {
// clear Modified flag
ULONG i;
Lba -= firstLba;
for(i=0; i<n; i++) {
WCacheClrModFlag(block_array, Lba+i);
}
}
}
return status;
} // end WCacheFlushBlocksRAM()
/*
WCacheCheckLimitsRAM() implements automatic flush and purge of
unused blocks to keep enough free cache entries for newly
read/written blocks for Random Access media.
See also WCacheCheckLimits()
Internal routine
*/
OSSTATUS
__fastcall
WCacheCheckLimitsRAM(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t ReqLba, // first LBA to access/cache
IN ULONG BCount // number of Blocks to access/cache
)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t lastLba;
lba_t Lba;
// PCHAR tmp_buff = Cache->tmp_buff;
ULONG firstPos;
ULONG lastPos;
// ULONG BSh = Cache->BlockSizeSh;
// ULONG BS = Cache->BlockSize;
// ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
// ULONG try_count = 0;
PW_CACHE_ENTRY block_array;
// OSSTATUS status;
ULONG FreeFrameCount = 0;
// PVOID addr;
if(Cache->FrameCount >= Cache->MaxFrames) {
FreeFrameCount = Cache->FramesToKeepFree;
} else
if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
// we need free space to grow WCache without flushing data
// for some period of time
FreeFrameCount = Cache->FramesToKeepFree;
goto Try_Another_Frame;
}
// remove(flush) some frames
while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) {
ASSERT(Cache->FrameCount <= Cache->MaxFrames);
Try_Another_Frame:
if(!Cache->FrameCount || !Cache->BlockCount) {
ASSERT(!Cache->FrameCount);
ASSERT(!Cache->BlockCount);
if(!Cache->FrameCount)
break;
}
frame = WCacheFindFrameToRelease(Cache);
#if 0
if(Cache->FrameList[frame].WriteCount) {
try_count++;
if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame;
} else {
try_count = 0;
}
#else
/*
if(Cache->FrameList[frame].UpdateCount) {
try_count = MAX_TRIES_FOR_NA;
} else {
try_count = 0;
}
*/
#endif
if(FreeFrameCount)
FreeFrameCount--;
firstLba = frame << Cache->BlocksPerFrameSh;
lastLba = firstLba + Cache->BlocksPerFrame;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
UDFPrint(("Hmm...\n"));
BrutePoint();
return STATUS_DRIVER_INTERNAL_ERROR;
}
WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
WCacheRemoveFrame(Cache, Context, frame);
}
// check if we try to read too much data
if(BCount > Cache->MaxBlocks) {
return STATUS_INVALID_PARAMETER;
}
// remove(flush) packet
while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
// try_count = 0;
//Try_Another_Block:
ASSERT(Cache->FrameCount <= Cache->MaxFrames);
Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1);
if(Lba == WCACHE_INVALID_LBA) {
ASSERT(!Cache->FrameCount);
ASSERT(!Cache->BlockCount);
break;
}
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
ASSERT(FALSE);
return STATUS_DRIVER_INTERNAL_ERROR;
}
WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
// check if frame is empty
if(!(Cache->FrameList[frame].BlockCount)) {
WCacheRemoveFrame(Cache, Context, frame);
} else {
ASSERT(Cache->FrameList[frame].Frame);
}
}
return STATUS_SUCCESS;
} // end WCacheCheckLimitsRAM()
/*
WCachePurgeAllRAM()
Internal routine
*/
OSSTATUS
__fastcall
WCachePurgeAllRAM(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context // user-supplied context for IO callbacks
)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t lastLba;
ULONG firstPos;
ULONG lastPos;
PW_CACHE_ENTRY block_array;
// OSSTATUS status;
// remove(flush) some frames
while(Cache->FrameCount) {
frame = Cache->CachedFramesList[0];
firstLba = frame << Cache->BlocksPerFrameSh;
lastLba = firstLba + Cache->BlocksPerFrame;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
UDFPrint(("Hmm...\n"));
BrutePoint();
return STATUS_DRIVER_INTERNAL_ERROR;
}
WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
WCacheRemoveFrame(Cache, Context, frame);
}
ASSERT(!Cache->FrameCount);
ASSERT(!Cache->BlockCount);
return STATUS_SUCCESS;
} // end WCachePurgeAllRAM()
/*
WCacheFlushAllRAM()
Internal routine
*/
OSSTATUS
__fastcall
WCacheFlushAllRAM(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context // user-supplied context for IO callbacks
)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t lastLba;
ULONG firstPos;
ULONG lastPos;
PW_CACHE_ENTRY block_array;
// OSSTATUS status;
// flush frames
while(Cache->WriteCount) {
frame = Cache->CachedModifiedBlocksList[0] >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
lastLba = firstLba + Cache->BlocksPerFrame;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
UDFPrint(("Hmm...\n"));
BrutePoint();
return STATUS_DRIVER_INTERNAL_ERROR;
}
WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, FALSE);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
}
return STATUS_SUCCESS;
} // end WCacheFlushAllRAM()
/*
WCachePreReadPacket__() reads & caches the whole packet containing
requested LBA. This routine just caches data, it doesn't copy anything
to user buffer.
In general we have no user buffer here... ;)
Public routine
*/
OSSTATUS
WCachePreReadPacket__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t Lba // LBA to cache together with whole packet
)
{
ULONG frame;
OSSTATUS status = STATUS_SUCCESS;
PW_CACHE_ENTRY block_array;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
PCHAR addr;
SIZE_T _ReadBytes;
ULONG PS = Cache->PacketSize; // (in blocks)
ULONG BCount = PS;
ULONG i, n, err_count;
BOOLEAN sector_added = FALSE;
ULONG block_type;
BOOLEAN zero = FALSE;//TRUE;
/*
ULONG first_zero=0, last_zero=0;
BOOLEAN count_first_zero = TRUE;
*/
Lba &= ~(PS-1);
frame = Lba >> Cache->BlocksPerFrameSh;
i = Lba - (frame << Cache->BlocksPerFrameSh);
// assume successful operation
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = WCacheInitFrame(Cache, Context, frame);
if(!block_array)
return STATUS_INSUFFICIENT_RESOURCES;
}
// skip cached extent (if any)
n=0;
while((n < BCount) &&
(n < Cache->BlocksPerFrame)) {
addr = (PCHAR)WCacheSectorAddr(block_array, i+n);
block_type = Cache->CheckUsedProc(Context, Lba+n);
if(/*WCacheGetBadFlag(block_array,i+n)*/
block_type & WCACHE_BLOCK_BAD) {
// bad packet. no pre-read
return STATUS_DEVICE_DATA_ERROR;
}
if(!(block_type & WCACHE_BLOCK_ZERO)) {
zero = FALSE;
//count_first_zero = FALSE;
//last_zero = 0;
if(!addr) {
// sector is not cached, stop search
break;
}
} else {
/*
if(count_first_zero) {
first_zero++;
}
last_zero++;
*/
}
n++;
}
// do nothing if all sectors are already cached
if(n < BCount) {
// read whole packet
if(!zero) {
status = Cache->ReadProc(Context, Cache->tmp_buff_r, PS<<BSh, Lba, &_ReadBytes, PH_TMP_BUFFER);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, PS, Cache->tmp_buff_r, WCACHE_R_OP, NULL);
}
} else {
status = STATUS_SUCCESS;
//RtlZeroMemory(Cache->tmp_buff_r, PS<<BSh);
_ReadBytes = PS<<BSh;
}
if(OS_SUCCESS(status)) {
// and now we'll copy them to cache
for(n=0; n<BCount; n++, i++) {
if(WCacheSectorAddr(block_array,i)) {
continue;
}
addr = block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
if(!addr) {
BrutePoint();
break;
}
sector_added = TRUE;
if(!zero) {
DbgCopyMemory(addr, Cache->tmp_buff_r+(n<<BSh), BS);
} else {
RtlZeroMemory(addr, BS);
}
Cache->FrameList[frame].BlockCount++;
}
} else {
// read sectors one by one and copy them to cache
// unreadable sectors will be treated as zero-filled
err_count = 0;
for(n=0; n<BCount; n++, i++) {
if(WCacheSectorAddr(block_array,i)) {
continue;
}
addr = block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
if(!addr) {
BrutePoint();
break;
}
sector_added = TRUE;
status = Cache->ReadProc(Context, Cache->tmp_buff_r, BS, Lba+n, &_ReadBytes, PH_TMP_BUFFER);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba+n, 1, Cache->tmp_buff_r, WCACHE_R_OP, NULL);
if(!OS_SUCCESS(status)) {
err_count++;
}
}
if(!zero && OS_SUCCESS(status)) {
DbgCopyMemory(addr, Cache->tmp_buff_r, BS);
} else
if(Cache->RememberBB) {
RtlZeroMemory(addr, BS);
/*
if(!OS_SUCCESS(status)) {
WCacheSetBadFlag(block_array,i);
}
*/
}
Cache->FrameList[frame].BlockCount++;
if(err_count >= 2) {
break;
}
}
// _ReadBytes = n<<BSh;
}
}
// we know the number of unread sectors if an error occured
// so we can need to update BlockCount
// return number of read bytes
if(sector_added)
WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, n);
return status;
} // end WCachePreReadPacket__()
/*
WCacheReadBlocks__() reads data from cache or
read it form media and store in cache.
Public routine
*/
OSSTATUS
WCacheReadBlocks__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN PCHAR Buffer, // user-supplied buffer for read blocks
IN lba_t Lba, // LBA to start read from
IN ULONG BCount, // number of blocks to be read
OUT PSIZE_T ReadBytes, // user-supplied pointer to ULONG that will
// recieve number of actually read bytes
IN BOOLEAN CachedOnly // specifies that cache is already locked
)
{
ULONG frame;
ULONG i, saved_i, saved_BC = BCount, n;
OSSTATUS status = STATUS_SUCCESS;
PW_CACHE_ENTRY block_array;
ULONG BSh = Cache->BlockSizeSh;
SIZE_T BS = Cache->BlockSize;
PCHAR addr;
ULONG to_read, saved_to_read;
// PCHAR saved_buff = Buffer;
SIZE_T _ReadBytes;
ULONG PS = Cache->PacketSize;
ULONG MaxR = Cache->MaxBytesToRead;
ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n
ULONG d;
ULONG block_type;
WcPrint(("WC:R %x (%x)\n", Lba, BCount));
(*ReadBytes) = 0;
// check if we try to read too much data
if(BCount >= Cache->MaxBlocks) {
i = 0;
if(CachedOnly) {
status = STATUS_INVALID_PARAMETER;
goto EO_WCache_R2;
}
while(TRUE) {
status = WCacheReadBlocks__(Cache, Context, Buffer + (i<<BSh), Lba, PS, &_ReadBytes, FALSE);
(*ReadBytes) += _ReadBytes;
if(!OS_SUCCESS(status) || (BCount <= PS)) break;
BCount -= PS;
Lba += PS;
i += PS;
}
return status;
}
// check if we try to access beyond cached area
if((Lba < Cache->FirstLba) ||
(Lba + BCount - 1 > Cache->LastLba)) {
status = Cache->ReadProc(Context, Buffer, BCount, Lba, ReadBytes, 0);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_R_OP, NULL);
}
return status;
}
if(!CachedOnly) {
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
}
frame = Lba >> Cache->BlocksPerFrameSh;
i = Lba - (frame << Cache->BlocksPerFrameSh);
if(Cache->CacheWholePacket && (BCount < PS)) {
if(!CachedOnly &&
!OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba & ~(PS-1), PS*2)) ) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return status;
}
} else {
if(!CachedOnly &&
!OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return status;
}
}
if(!CachedOnly) {
// convert to shared
// ExConvertExclusiveToSharedLite(&(Cache->WCacheLock));
}
// pre-read packet. It is very useful for
// highly fragmented files
if(Cache->CacheWholePacket && (BCount < PS)) {
// status = WCacheReadBlocks__(Cache, Context, Cache->tmp_buff_r, Lba & (~PacketMask), PS, &_ReadBytes, TRUE);
// we should not perform IO if user requested CachedOnly data
if(!CachedOnly) {
status = WCachePreReadPacket__(Cache, Context, Lba);
}
status = STATUS_SUCCESS;
}
// assume successful operation
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
ASSERT(!CachedOnly);
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = WCacheInitFrame(Cache, Context, frame);
if(!block_array) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_R;
}
}
Cache->FrameList[frame].AccessCount++;
while(BCount) {
if(i >= Cache->BlocksPerFrame) {
frame++;
block_array = Cache->FrameList[frame].Frame;
i -= Cache->BlocksPerFrame;
}
if(!block_array) {
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = WCacheInitFrame(Cache, Context, frame);
if(!block_array) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_R;
}
}
// 'read' cached extent (if any)
// it is just copying
while(BCount &&
(i < Cache->BlocksPerFrame) &&
(addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount);
if(block_type & WCACHE_BLOCK_BAD) {
//if(WCacheGetBadFlag(block_array,i)) {
status = STATUS_DEVICE_DATA_ERROR;
goto EO_WCache_R;
}
DbgCopyMemory(Buffer, addr, BS);
Buffer += BS;
*ReadBytes += BS;
i++;
BCount--;
}
// read non-cached packet-size-aligned extent (if any)
// now we'll calculate total length & decide if it has enough size
if(!((d = Lba+saved_BC-BCount) & PacketMask) && d ) {
n = 0;
while(BCount &&
(i < Cache->BlocksPerFrame) &&
(!WCacheSectorAddr(block_array, i)) ) {
n++;
BCount--;
}
BCount += n;
n &= ~PacketMask;
if(n>PS) {
if(!OS_SUCCESS(status = Cache->ReadProc(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_ReadBytes, 0))) {
status = WCacheRaiseIoError(Cache, Context, status, Lba+saved_BC-BCount, n, Buffer, WCACHE_R_OP, NULL);
if(!OS_SUCCESS(status)) {
goto EO_WCache_R;
}
}
// WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
BCount -= n;
Lba += saved_BC - BCount;
saved_BC = BCount;
i += n;
Buffer += BS*n;
*ReadBytes += BS*n;
}
// } else {
// UDFPrint(("Unaligned\n"));
}
// read non-cached extent (if any)
// firstable, we'll get total number of sectors to read
to_read = 0;
saved_i = i;
d = BCount;
while(d &&
(i < Cache->BlocksPerFrame) &&
(!WCacheSectorAddr(block_array, i)) ) {
i++;
to_read += BS;
d--;
}
// read some not cached sectors
if(to_read) {
i = saved_i;
saved_to_read = to_read;
d = BCount - d;
// split request if necessary
if(saved_to_read > MaxR) {
WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
n = MaxR >> BSh;
do {
status = Cache->ReadProc(Context, Buffer, MaxR, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0);
*ReadBytes += _ReadBytes;
if(!OS_SUCCESS(status)) {
_ReadBytes &= ~(BS-1);
BCount -= _ReadBytes >> BSh;
saved_to_read -= _ReadBytes;
Buffer += _ReadBytes;
saved_BC = BCount;
goto store_read_data_1;
}
Buffer += MaxR;
saved_to_read -= MaxR;
i += n;
BCount -= n;
d -= n;
} while(saved_to_read > MaxR);
saved_BC = BCount;
}
if(saved_to_read) {
status = Cache->ReadProc(Context, Buffer, saved_to_read, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0);
*ReadBytes += _ReadBytes;
if(!OS_SUCCESS(status)) {
_ReadBytes &= ~(BS-1);
BCount -= _ReadBytes >> BSh;
saved_to_read -= _ReadBytes;
Buffer += _ReadBytes;
goto store_read_data_1;
}
Buffer += saved_to_read;
saved_to_read = 0;
BCount -= d;
}
store_read_data_1:
// and now we'll copy them to cache
//
Buffer -= (to_read - saved_to_read);
i = saved_i;
while(to_read - saved_to_read) {
block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
if(!block_array[i].Sector) {
BCount += to_read >> BSh;
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_R;
}
DbgCopyMemory(block_array[i].Sector, Buffer, BS);
Cache->FrameList[frame].BlockCount++;
i++;
Buffer += BS;
to_read -= BS;
}
if(!OS_SUCCESS(status))
goto EO_WCache_R;
to_read = 0;
}
}
EO_WCache_R:
// we know the number of unread sectors if an error occured
// so we can need to update BlockCount
// return number of read bytes
WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
// Cache->FrameList[frame].BlockCount -= BCount;
EO_WCache_R2:
if(!CachedOnly) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
}
return status;
} // end WCacheReadBlocks__()
/*
WCacheWriteBlocks__() writes data to cache.
Data is written directly to media if:
1) requested block is Packet-aligned
2) requested Lba(s) lays beyond cached area
Public routine
*/
OSSTATUS
WCacheWriteBlocks__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN PCHAR Buffer, // user-supplied buffer containing data to be written
IN lba_t Lba, // LBA to start write from
IN ULONG BCount, // number of blocks to be written
OUT PSIZE_T WrittenBytes, // user-supplied pointer to ULONG that will
// recieve number of actually written bytes
IN BOOLEAN CachedOnly // specifies that cache is already locked
)
{
ULONG frame;
ULONG i, saved_BC = BCount, n, d;
OSSTATUS status = STATUS_SUCCESS;
PW_CACHE_ENTRY block_array;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
PCHAR addr;
// PCHAR saved_buff = Buffer;
SIZE_T _WrittenBytes;
ULONG PS = Cache->PacketSize;
ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n
ULONG block_type;
// BOOLEAN Aligned = FALSE;
BOOLEAN WriteThrough = FALSE;
lba_t WTh_Lba;
ULONG WTh_BCount;
WcPrint(("WC:W %x (%x)\n", Lba, BCount));
*WrittenBytes = 0;
// UDFPrint(("BCount:%x\n",BCount));
// check if we try to read too much data
if(BCount >= Cache->MaxBlocks) {
i = 0;
if(CachedOnly) {
status = STATUS_INVALID_PARAMETER;
goto EO_WCache_W2;
}
while(TRUE) {
// UDFPrint((" BCount:%x\n",BCount));
status = WCacheWriteBlocks__(Cache, Context, Buffer + (i<<BSh), Lba, min(PS,BCount), &_WrittenBytes, FALSE);
(*WrittenBytes) += _WrittenBytes;
BCount -= PS;
Lba += PS;
i += PS;
if(!OS_SUCCESS(status) || (BCount < PS))
return status;
}
}
// check if we try to access beyond cached area
if((Lba < Cache->FirstLba) ||
(Lba + BCount - 1 > Cache->LastLba)) {
return STATUS_INVALID_PARAMETER;
}
if(!CachedOnly) {
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
}
frame = Lba >> Cache->BlocksPerFrameSh;
i = Lba - (frame << Cache->BlocksPerFrameSh);
if(!CachedOnly &&
!OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return status;
}
// assume successful operation
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
if(BCount && !(BCount & (PS-1)) && !(Lba & (PS-1)) &&
(Cache->Mode != WCACHE_MODE_R) &&
(i+BCount <= Cache->BlocksPerFrame) &&
!Cache->NoWriteThrough) {
status = Cache->WriteProc(Context, Buffer, BCount<<BSh, Lba, WrittenBytes, 0);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_W_OP, NULL);
}
goto EO_WCache_W2;
}
ASSERT(!CachedOnly);
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = WCacheInitFrame(Cache, Context, frame);
if(!block_array) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_W;
}
}
if(Cache->Mode == WCACHE_MODE_RAM &&
BCount &&
// !(Lba & (PS-1)) &&
(!(BCount & (PS-1)) || (BCount > PS)) ) {
WriteThrough = TRUE;
WTh_Lba = Lba;
WTh_BCount = BCount;
} else
if(Cache->Mode == WCACHE_MODE_RAM &&
((Lba & ~PacketMask) != ((Lba+BCount-1) & ~PacketMask))
) {
WriteThrough = TRUE;
WTh_Lba = Lba & ~PacketMask;
WTh_BCount = PS;
}
Cache->FrameList[frame].UpdateCount++;
// UDFPrint((" BCount:%x\n",BCount));
while(BCount) {
if(i >= Cache->BlocksPerFrame) {
frame++;
block_array = Cache->FrameList[frame].Frame;
i -= Cache->BlocksPerFrame;
}
if(!block_array) {
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = WCacheInitFrame(Cache, Context, frame);
if(!block_array) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_W;
}
}
// 'write' cached extent (if any)
// it is just copying
while(BCount &&
(i < Cache->BlocksPerFrame) &&
(addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
// UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",addr, Buffer, BS, BCount));
block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount);
if(Cache->NoWriteBB &&
/*WCacheGetBadFlag(block_array,i)*/
(block_type & WCACHE_BLOCK_BAD)) {
// bad packet. no cached write
status = STATUS_DEVICE_DATA_ERROR;
goto EO_WCache_W;
}
DbgCopyMemory(addr, Buffer, BS);
WCacheSetModFlag(block_array, i);
Buffer += BS;
*WrittenBytes += BS;
i++;
BCount--;
}
// write non-cached not-aligned extent (if any) till aligned one
while(BCount &&
(i & PacketMask) &&
(Cache->Mode != WCACHE_MODE_R) &&
(i < Cache->BlocksPerFrame) &&
(!WCacheSectorAddr(block_array, i)) ) {
block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
if(!block_array[i].Sector) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_W;
}
// UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount));
DbgCopyMemory(block_array[i].Sector, Buffer, BS);
WCacheSetModFlag(block_array, i);
i++;
Buffer += BS;
*WrittenBytes += BS;
BCount--;
Cache->FrameList[frame].BlockCount ++;
}
// write non-cached packet-size-aligned extent (if any)
// now we'll calculate total length & decide if has enough size
if(!Cache->NoWriteThrough
&&
( !(i & PacketMask) ||
((Cache->Mode == WCACHE_MODE_R) && (BCount >= PS)) )) {
n = 0;
while(BCount &&
(i < Cache->BlocksPerFrame) &&
(!WCacheSectorAddr(block_array, i)) ) {
n++;
BCount--;
}
BCount += n;
n &= ~PacketMask;
// if(!OS_SUCCESS(status = Cache->WriteProcAsync(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_WrittenBytes, FALSE)))
if(n) {
// add previously written data to list
d = saved_BC - BCount;
WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, d);
WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d);
Lba += d;
saved_BC = BCount;
while(n) {
if(Cache->Mode == WCACHE_MODE_R)
Cache->UpdateRelocProc(Context, Lba, NULL, PS);
if(!OS_SUCCESS(status = Cache->WriteProc(Context, Buffer, PS<<BSh, Lba, &_WrittenBytes, 0))) {
status = WCacheRaiseIoError(Cache, Context, status, Lba, PS, Buffer, WCACHE_W_OP, NULL);
if(!OS_SUCCESS(status)) {
goto EO_WCache_W;
}
}
BCount -= PS;
Lba += PS;
saved_BC = BCount;
i += PS;
Buffer += PS<<BSh;
*WrittenBytes += PS<<BSh;
n-=PS;
}
}
}
// write non-cached not-aligned extent (if any)
while(BCount &&
(i < Cache->BlocksPerFrame) &&
(!WCacheSectorAddr(block_array, i)) ) {
block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
if(!block_array[i].Sector) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_W;
}
// UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount));
DbgCopyMemory(block_array[i].Sector, Buffer, BS);
WCacheSetModFlag(block_array, i);
i++;
Buffer += BS;
*WrittenBytes += BS;
BCount--;
Cache->FrameList[frame].BlockCount ++;
}
}
EO_WCache_W:
// we know the number of unread sectors if an error occured
// so we can need to update BlockCount
// return number of read bytes
WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, saved_BC - BCount);
if(WriteThrough && !BCount) {
ULONG d;
// lba_t lastLba;
ULONG firstPos;
ULONG lastPos;
BCount = WTh_BCount;
Lba = WTh_Lba;
while(BCount) {
frame = Lba >> Cache->BlocksPerFrameSh;
// firstLba = frame << Cache->BlocksPerFrameSh;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba);
d = min(Lba+BCount, (frame+1) << Cache->BlocksPerFrameSh) - Lba;
lastPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba+d);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
ASSERT(FALSE);
BCount -= d;
Lba += d;
continue;
}
status = WCacheFlushBlocksRAM(Cache, Context, block_array, Cache->CachedBlocksList, firstPos, lastPos, FALSE);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d);
BCount -= d;
Lba += d;
}
}
EO_WCache_W2:
if(!CachedOnly) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
}
return status;
} // end WCacheWriteBlocks__()
/*
WCacheFlushAll__() copies all data stored in cache to media.
Flushed blocks are kept in cache.
Public routine
*/
VOID
WCacheFlushAll__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context) // user-supplied context for IO callbacks
{
if(!(Cache->ReadProc)) return;
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
switch(Cache->Mode) {
case WCACHE_MODE_RAM:
WCacheFlushAllRAM(Cache, Context);
break;
case WCACHE_MODE_ROM:
case WCACHE_MODE_RW:
WCacheFlushAllRW(Cache, Context);
break;
case WCACHE_MODE_R:
WCachePurgeAllR(Cache, Context);
break;
}
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return;
} // end WCacheFlushAll__()
/*
WCachePurgeAll__() copies all data stored in cache to media.
Flushed blocks are removed cache.
Public routine
*/
VOID
WCachePurgeAll__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context) // user-supplied context for IO callbacks
{
if(!(Cache->ReadProc)) return;
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
switch(Cache->Mode) {
case WCACHE_MODE_RAM:
WCachePurgeAllRAM(Cache, Context);
break;
case WCACHE_MODE_ROM:
case WCACHE_MODE_RW:
WCachePurgeAllRW(Cache, Context);
break;
case WCACHE_MODE_R:
WCachePurgeAllR(Cache, Context);
break;
}
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return;
} // end WCachePurgeAll__()
/*
WCachePurgeAllRW() copies modified blocks from cache to media
and removes them from cache
This routine can be used for RAM, RW and ROM media.
For ROM media blocks are just removed.
Internal routine
*/
VOID
__fastcall
WCachePurgeAllRW(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context) // user-supplied context for IO callbacks
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t Lba;
// ULONG firstPos;
// ULONG lastPos;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
PW_CACHE_ENTRY block_array;
// OSSTATUS status;
SIZE_T ReadBytes;
PW_CACHE_ASYNC FirstWContext = NULL;
PW_CACHE_ASYNC PrevWContext = NULL;
ULONG chain_count = 0;
if(!(Cache->ReadProc)) return;
while(Cache->BlockCount) {
Lba = List[0] & ~(PSs-1);
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
// firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
// lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
BrutePoint();
return;
}
WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
// free memory
WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
// check if frame is empty
if(!(Cache->FrameList[frame].BlockCount)) {
WCacheRemoveFrame(Cache, Context, frame);
} else {
ASSERT(Cache->FrameList[frame].Frame);
}
chain_count++;
if(chain_count >= WCACHE_MAX_CHAIN) {
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
chain_count = 0;
}
}
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
return;
} // end WCachePurgeAllRW()
/*
WCacheFlushAllRW() copies modified blocks from cache to media.
All blocks are not removed from cache.
This routine can be used for RAM, RW and ROM media.
Internal routine
*/
VOID
__fastcall
WCacheFlushAllRW(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context) // user-supplied context for IO callbacks
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedModifiedBlocksList;
lba_t Lba;
// ULONG firstPos;
// ULONG lastPos;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
ULONG BFs = Cache->BlocksPerFrameSh;
PW_CACHE_ENTRY block_array;
// OSSTATUS status;
SIZE_T ReadBytes;
PW_CACHE_ASYNC FirstWContext = NULL;
PW_CACHE_ASYNC PrevWContext = NULL;
ULONG i;
ULONG chain_count = 0;
if(!(Cache->ReadProc)) return;
// walk through modified blocks
while(Cache->WriteCount) {
Lba = List[0] & ~(PSs-1);
frame = Lba >> BFs;
firstLba = frame << BFs;
// firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba);
// lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
BrutePoint();
continue;;
}
// queue modify request
WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
// clear MODIFIED flag for queued blocks
WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs);
Lba -= firstLba;
for(i=0; i<PSs; i++) {
WCacheClrModFlag(block_array, Lba+i);
}
chain_count++;
// check queue size
if(chain_count >= WCACHE_MAX_CHAIN) {
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
chain_count = 0;
}
}
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
#ifdef DBG
#if 1
// check consistency
List = Cache->CachedBlocksList;
for(i=0; i<Cache->BlockCount; i++) {
Lba = List[i] /*& ~(PSs-1)*/;
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
BrutePoint();
}
ASSERT(!WCacheGetModFlag(block_array, Lba-firstLba));
}
#endif // 1
#endif // DBG
return;
} // end WCacheFlushAllRW()
/*
WCacheRelease__() frees all allocated memory blocks and
deletes synchronization resources
Public routine
*/
VOID
WCacheRelease__(
IN PW_CACHE Cache // pointer to the Cache Control structure
)
{
ULONG i, j, k;
PW_CACHE_ENTRY block_array;
Cache->Tag = 0xDEADCACE;
if(!(Cache->ReadProc)) return;
// ASSERT(Cache->Tag == 0xCAC11E00);
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
for(i=0; i<Cache->FrameCount; i++) {
j = Cache->CachedFramesList[i];
block_array = Cache->FrameList[j].Frame;
if(block_array) {
for(k=0; k<Cache->BlocksPerFrame; k++) {
if(WCacheSectorAddr(block_array, k)) {
WCacheFreeSector(j, k);
}
}
MyFreePool__(block_array);
}
}
if(Cache->FrameList)
MyFreePool__(Cache->FrameList);
if(Cache->CachedBlocksList)
MyFreePool__(Cache->CachedBlocksList);
if(Cache->CachedModifiedBlocksList)
MyFreePool__(Cache->CachedModifiedBlocksList);
if(Cache->CachedFramesList)
MyFreePool__(Cache->CachedFramesList);
if(Cache->tmp_buff_r)
MyFreePool__(Cache->tmp_buff_r);
if(Cache->CachedFramesList)
MyFreePool__(Cache->tmp_buff);
if(Cache->CachedFramesList)
MyFreePool__(Cache->reloc_tab);
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
ExDeleteResourceLite(&(Cache->WCacheLock));
RtlZeroMemory(Cache, sizeof(W_CACHE));
return;
} // end WCacheRelease__()
/*
WCacheIsInitialized__() checks if the pointer supplied points
to initialized cache structure.
Public routine
*/
BOOLEAN
WCacheIsInitialized__(
IN PW_CACHE Cache
)
{
return (Cache->ReadProc != NULL);
} // end WCacheIsInitialized__()
OSSTATUS
WCacheFlushBlocksRW(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t _Lba, // LBA to start flush from
IN ULONG BCount // number of blocks to be flushed
)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedModifiedBlocksList;
lba_t Lba;
// ULONG firstPos;
// ULONG lastPos;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
ULONG BFs = Cache->BlocksPerFrameSh;
PW_CACHE_ENTRY block_array;
// OSSTATUS status;
SIZE_T ReadBytes;
PW_CACHE_ASYNC FirstWContext = NULL;
PW_CACHE_ASYNC PrevWContext = NULL;
ULONG i;
ULONG chain_count = 0;
lba_t lim;
if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER;
// walk through modified blocks
lim = (_Lba+BCount+PSs-1) & ~(PSs-1);
for(Lba = _Lba & ~(PSs-1);Lba < lim ; Lba += PSs) {
frame = Lba >> BFs;
firstLba = frame << BFs;
// firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba);
// lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
// not cached block may be requested for flush
Lba += (1 << BFs) - PSs;
continue;
}
// queue modify request
WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
// clear MODIFIED flag for queued blocks
WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs);
Lba -= firstLba;
for(i=0; i<PSs; i++) {
WCacheClrModFlag(block_array, Lba+i);
}
Lba += firstLba;
chain_count++;
// check queue size
if(chain_count >= WCACHE_MAX_CHAIN) {
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
chain_count = 0;
}
}
WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
/*
if(Cache->Mode != WCACHE_MODE_RAM)
return STATUS_SUCCESS;
*/
return STATUS_SUCCESS;
} // end WCacheFlushBlocksRW()
/*
WCacheFlushBlocks__() copies specified blocks stored in cache to media.
Flushed blocks are kept in cache.
Public routine
*/
OSSTATUS
WCacheFlushBlocks__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t Lba, // LBA to start flush from
IN ULONG BCount // number of blocks to be flushed
)
{
OSSTATUS status;
if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER;
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
// check if we try to access beyond cached area
if((Lba < Cache->FirstLba) ||
(Lba+BCount-1 > Cache->LastLba)) {
UDFPrint(("LBA %#x (%x) is beyond cacheable area\n", Lba, BCount));
BrutePoint();
status = STATUS_INVALID_PARAMETER;
goto EO_WCache_F;
}
switch(Cache->Mode) {
case WCACHE_MODE_RAM:
// WCacheFlushBlocksRW(Cache, Context);
// break;
case WCACHE_MODE_ROM:
case WCACHE_MODE_RW:
status = WCacheFlushBlocksRW(Cache, Context, Lba, BCount);
break;
case WCACHE_MODE_R:
status = STATUS_SUCCESS;
break;
}
EO_WCache_F:
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return status;
} // end WCacheFlushBlocks__()
/*
WCacheDirect__() returns pointer to memory block where
requested block is stored in.
If no #CachedOnly flag specified this routine locks cache,
otherwise it assumes that cache is already locked by previous call
to WCacheStartDirect__().
Cache can be unlocked by WCacheEODirect__().
Using this routine caller can access cached block directly in memory
without Read_to_Tmp and Modify/Write steps.
Public routine
*/
OSSTATUS
WCacheDirect__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t Lba, // LBA of block to get pointer to
IN BOOLEAN Modified, // indicates that block will be modified
OUT PCHAR* CachedBlock, // address for pointer to cached block to be stored in
IN BOOLEAN CachedOnly // specifies that cache is already locked
)
{
ULONG frame;
ULONG i;
OSSTATUS status = STATUS_SUCCESS;
PW_CACHE_ENTRY block_array;
ULONG BS = Cache->BlockSize;
PCHAR addr;
SIZE_T _ReadBytes;
ULONG block_type;
WcPrint(("WC:%sD %x (1)\n", Modified ? "W" : "R", Lba));
// lock cache if nececcary
if(!CachedOnly) {
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
}
// check if we try to access beyond cached area
if((Lba < Cache->FirstLba) ||
(Lba > Cache->LastLba)) {
UDFPrint(("LBA %#x is beyond cacheable area\n", Lba));
BrutePoint();
status = STATUS_INVALID_PARAMETER;
goto EO_WCache_D;
}
frame = Lba >> Cache->BlocksPerFrameSh;
i = Lba - (frame << Cache->BlocksPerFrameSh);
// check if we have enough space to store requested block
if(!CachedOnly &&
!OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, 1))) {
BrutePoint();
goto EO_WCache_D;
}
// small updates are more important
block_array = Cache->FrameList[frame].Frame;
if(Modified) {
Cache->FrameList[frame].UpdateCount+=8;
} else {
Cache->FrameList[frame].AccessCount+=8;
}
if(!block_array) {
ASSERT(Cache->FrameCount < Cache->MaxFrames);
block_array = WCacheInitFrame(Cache, Context, frame);
if(!block_array) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_D;
}
}
// check if requested block is already cached
if( !(addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
// block is not cached
// allocate memory and read block from media
// do not set block_array[i].Sector here, because if media access fails and recursive access to cache
// comes, this block should not be marked as 'cached'
addr = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
if(!addr) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto EO_WCache_D;
}
block_type = Cache->CheckUsedProc(Context, Lba);
if(block_type == WCACHE_BLOCK_USED) {
status = Cache->ReadProc(Context, addr, BS, Lba, &_ReadBytes, PH_TMP_BUFFER);
if(Cache->RememberBB) {
if(!OS_SUCCESS(status)) {
RtlZeroMemory(addr, BS);
//WCacheSetBadFlag(block_array,i);
}
}
} else {
if(block_type & WCACHE_BLOCK_BAD) {
DbgFreePool(addr);
addr = NULL;
status = STATUS_DEVICE_DATA_ERROR;
goto EO_WCache_D;
}
if(!(block_type & WCACHE_BLOCK_ZERO)) {
BrutePoint();
}
status = STATUS_SUCCESS;
RtlZeroMemory(addr, BS);
}
// now add pointer to buffer to common storage
block_array[i].Sector = addr;
WCacheInsertItemToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba);
if(Modified) {
WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
WCacheSetModFlag(block_array, i);
}
Cache->FrameList[frame].BlockCount ++;
} else {
// block is not cached
// just return pointer
block_type = Cache->CheckUsedProc(Context, Lba);
if(block_type & WCACHE_BLOCK_BAD) {
//if(WCacheGetBadFlag(block_array,i)) {
// bad packet. no pre-read
status = STATUS_DEVICE_DATA_ERROR;
goto EO_WCache_D;
}
#ifndef UDF_CHECK_UTIL
ASSERT(block_type & WCACHE_BLOCK_USED);
#else
if(!(block_type & WCACHE_BLOCK_USED)) {
UDFPrint(("LBA %#x is not marked as used\n", Lba));
}
#endif
if(Modified &&
!WCacheGetModFlag(block_array, i)) {
WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
WCacheSetModFlag(block_array, i);
}
}
(*CachedBlock) = addr;
EO_WCache_D:
return status;
} // end WCacheDirect__()
/*
WCacheEODirect__() must be used to unlock cache after calls to
to WCacheStartDirect__().
Public routine
*/
OSSTATUS
WCacheEODirect__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context // user-supplied context for IO callbacks
)
{
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return STATUS_SUCCESS;
} // end WCacheEODirect__()
/*
WCacheStartDirect__() locks cache for exclusive use.
Using this routine caller can access cached block directly in memory
without Read_to_Tmp and Modify/Write steps.
See also WCacheDirect__()
Cache can be unlocked by WCacheEODirect__().
Public routine
*/
OSSTATUS
WCacheStartDirect__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN BOOLEAN Exclusive // lock cache for exclusive use,
// currently must be TRUE.
)
{
if(Exclusive) {
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
} else {
BrutePoint();
ExAcquireResourceSharedLite(&(Cache->WCacheLock), TRUE);
}
return STATUS_SUCCESS;
} // end WCacheStartDirect__()
/*
WCacheIsCached__() checks if requested blocks are immediately available.
Cache must be previously locked for exclusive use with WCacheStartDirect__().
Using this routine caller can access cached block directly in memory
without Read_to_Tmp and Modify/Write steps.
See also WCacheDirect__().
Cache can be unlocked by WCacheEODirect__().
Public routine
*/
BOOLEAN
WCacheIsCached__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN lba_t Lba, // LBA to start check from
IN ULONG BCount // number of blocks to be checked
)
{
ULONG frame;
ULONG i;
PW_CACHE_ENTRY block_array;
// check if we try to access beyond cached area
if((Lba < Cache->FirstLba) ||
(Lba + BCount - 1 > Cache->LastLba)) {
return FALSE;
}
frame = Lba >> Cache->BlocksPerFrameSh;
i = Lba - (frame << Cache->BlocksPerFrameSh);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
return FALSE;
}
while(BCount) {
if(i >= Cache->BlocksPerFrame) {
frame++;
block_array = Cache->FrameList[frame].Frame;
i -= Cache->BlocksPerFrame;
}
if(!block_array) {
return FALSE;
}
// 'read' cached extent (if any)
while(BCount &&
(i < Cache->BlocksPerFrame) &&
WCacheSectorAddr(block_array, i) &&
/*!WCacheGetBadFlag(block_array, i)*/
/*!(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_BAD)*/
TRUE ) {
i++;
BCount--;
Lba++;
}
if(BCount &&
(i < Cache->BlocksPerFrame) /*&&
(!WCacheSectorAddr(block_array, i))*/ ) {
return FALSE;
}
}
return TRUE;
} // end WCacheIsCached__()
/*
WCacheCheckLimitsR() implements automatic flush and purge of
unused blocks to keep enough free cache entries for newly
read/written blocks for WORM media.
See also WCacheCheckLimits()
Internal routine
*/
OSSTATUS
__fastcall
WCacheCheckLimitsR(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context, // user-supplied context for IO callbacks
IN lba_t ReqLba, // first LBA to access/cache
IN ULONG BCount // number of Blocks to access/cache
)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t Lba;
PCHAR tmp_buff = Cache->tmp_buff;
ULONG firstPos;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
ULONG PSs = Cache->PacketSize;
ULONG i;
PW_CACHE_ENTRY block_array;
BOOLEAN mod;
OSSTATUS status;
SIZE_T ReadBytes;
ULONG MaxReloc = Cache->PacketSize;
PULONG reloc_tab = Cache->reloc_tab;
// check if we try to read too much data
if(BCount > Cache->MaxBlocks) {
return STATUS_INVALID_PARAMETER;
}
// remove(flush) packets from entire frame(s)
while( ((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) ||
(Cache->FrameCount >= Cache->MaxFrames) ) {
WCCL_retry_1:
Lba = WCacheFindLbaToRelease(Cache);
if(Lba == WCACHE_INVALID_LBA) {
ASSERT(!Cache->FrameCount);
ASSERT(!Cache->BlockCount);
break;
}
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
return STATUS_DRIVER_INTERNAL_ERROR;
}
// check if modified
mod = WCacheGetModFlag(block_array, Lba - firstLba);
// read/modify/write
if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
if(Cache->WriteCount < MaxReloc) goto WCCL_retry_1;
firstPos = WCacheGetSortedListIndex(Cache->WriteCount, Cache->CachedModifiedBlocksList, Lba);
if(!block_array) {
return STATUS_DRIVER_INTERNAL_ERROR;
}
// prepare packet & reloc table
for(i=0; i<MaxReloc; i++) {
Lba = Cache->CachedModifiedBlocksList[firstPos];
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
DbgCopyMemory(tmp_buff + (i << BSh),
(PVOID)WCacheSectorAddr(block_array, Lba-firstLba),
BS);
reloc_tab[i] = Lba;
WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
// mark as non-cached & free pool
WCacheFreeSector(frame, Lba-firstLba);
// check if frame is empty
if(!Cache->FrameList[frame].BlockCount) {
WCacheRemoveFrame(Cache, Context, frame);
}
if(firstPos >= Cache->WriteCount) firstPos=0;
}
// write packet
// status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE);
Cache->UpdateRelocProc(Context, NULL, reloc_tab, MaxReloc);
status = Cache->WriteProc(Context, tmp_buff, PS, NULL, &ReadBytes, 0);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, NULL, PSs, tmp_buff, WCACHE_W_OP, NULL);
}
} else {
if((i = Cache->BlockCount - Cache->WriteCount) > MaxReloc) i = MaxReloc;
// discard blocks
for(; i; i--) {
Lba = List[firstPos];
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
if( (mod = WCacheGetModFlag(block_array, Lba - firstLba)) &&
(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED) )
continue;
WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
if(mod)
WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
// mark as non-cached & free pool
WCacheFreeSector(frame, Lba-firstLba);
// check if frame is empty
if(!Cache->FrameList[frame].BlockCount) {
WCacheRemoveFrame(Cache, Context, frame);
}
if(firstPos >= Cache->WriteCount) firstPos=0;
}
}
}
return STATUS_SUCCESS;
} // end WCacheCheckLimitsR()
/*
WCachePurgeAllR() copies modified blocks from cache to media
and removes them from cache
This routine can be used for R media only.
Internal routine
*/
VOID
__fastcall
WCachePurgeAllR(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN PVOID Context) // user-supplied context for IO callbacks
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t Lba;
PCHAR tmp_buff = Cache->tmp_buff;
ULONG BSh = Cache->BlockSizeSh;
ULONG BS = Cache->BlockSize;
// ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
// ULONG PSs = Cache->PacketSize;
PW_CACHE_ENTRY block_array;
BOOLEAN mod;
OSSTATUS status;
SIZE_T ReadBytes;
ULONG MaxReloc = Cache->PacketSize;
PULONG reloc_tab = Cache->reloc_tab;
ULONG RelocCount = 0;
BOOLEAN IncompletePacket;
ULONG i=0;
ULONG PacketTail;
while(Cache->WriteCount < Cache->BlockCount) {
Lba = List[i];
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
BrutePoint();
return;
}
// check if modified
mod = WCacheGetModFlag(block_array, Lba - firstLba);
// just discard
if(!mod || !(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
// mark as non-cached & free pool
if(WCacheSectorAddr(block_array,Lba-firstLba)) {
WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
if(mod)
WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
// mark as non-cached & free pool
WCacheFreeSector(frame, Lba-firstLba);
// check if frame is empty
if(!Cache->FrameList[frame].BlockCount) {
WCacheRemoveFrame(Cache, Context, frame);
}
} else {
BrutePoint();
}
} else {
i++;
}
}
PacketTail = Cache->WriteCount & (MaxReloc-1);
IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE;
// remove(flush) packet
while((Cache->WriteCount > PacketTail) || (Cache->WriteCount && IncompletePacket)) {
Lba = List[0];
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
BrutePoint();
return;
}
// check if modified
mod = WCacheGetModFlag(block_array, Lba - firstLba);
// pack/reloc/write
if(mod) {
DbgCopyMemory(tmp_buff + (RelocCount << BSh),
(PVOID)WCacheSectorAddr(block_array, Lba-firstLba),
BS);
reloc_tab[RelocCount] = Lba;
RelocCount++;
// write packet
if((RelocCount >= MaxReloc) || (Cache->BlockCount == 1)) {
// status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE);
Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount);
status = Cache->WriteProc(Context, tmp_buff, RelocCount<<BSh, NULL, &ReadBytes, 0);
if(!OS_SUCCESS(status)) {
status = WCacheRaiseIoError(Cache, Context, status, NULL, RelocCount, tmp_buff, WCACHE_W_OP, NULL);
}
RelocCount = 0;
}
WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
} else {
BrutePoint();
}
// mark as non-cached & free pool
if(WCacheSectorAddr(block_array,Lba-firstLba)) {
WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
// mark as non-cached & free pool
WCacheFreeSector(frame, Lba-firstLba);
// check if frame is empty
if(!Cache->FrameList[frame].BlockCount) {
WCacheRemoveFrame(Cache, Context, frame);
}
} else {
BrutePoint();
}
}
} // end WCachePurgeAllR()
/*
WCacheSetMode__() changes cache operating mode (ROM/R/RW/RAM).
Public routine
*/
OSSTATUS
WCacheSetMode__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN ULONG Mode // cache mode/media type to be used
)
{
if(Mode > WCACHE_MODE_MAX) return STATUS_INVALID_PARAMETER;
Cache->Mode = Mode;
return STATUS_SUCCESS;
} // end WCacheSetMode__()
/*
WCacheGetMode__() returns cache operating mode (ROM/R/RW/RAM).
Public routine
*/
ULONG
WCacheGetMode__(
IN PW_CACHE Cache
)
{
return Cache->Mode;
} // end WCacheGetMode__()
/*
WCacheGetWriteBlockCount__() returns number of modified blocks, those are
not flushed to media. Is usually used to preallocate blocks for
relocation table on WORM (R) media.
Public routine
*/
ULONG
WCacheGetWriteBlockCount__(
IN PW_CACHE Cache
)
{
return Cache->WriteCount;
} // end WCacheGetWriteBlockCount__()
/*
WCacheSyncReloc__() builds list of all modified blocks, currently
stored in cache. For each modified block WCacheSyncReloc__() calls
user-supplied callback routine in order to update relocation table
on WORM (R) media.
Public routine
*/
VOID
WCacheSyncReloc__(
IN PW_CACHE Cache,
IN PVOID Context)
{
ULONG frame;
lba_t firstLba;
lba_t* List = Cache->CachedBlocksList;
lba_t Lba;
// ULONG BSh = Cache->BlockSizeSh;
// ULONG BS = Cache->BlockSize;
// ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
// ULONG PSs = Cache->PacketSize;
PW_CACHE_ENTRY block_array;
BOOLEAN mod;
ULONG MaxReloc = Cache->PacketSize;
PULONG reloc_tab = Cache->reloc_tab;
ULONG RelocCount = 0;
BOOLEAN IncompletePacket;
IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE;
// enumerate modified blocks
for(ULONG i=0; IncompletePacket && (i<Cache->BlockCount); i++) {
Lba = List[i];
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
return;
}
// check if modified
mod = WCacheGetModFlag(block_array, Lba - firstLba);
// update relocation table for modified sectors
if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
reloc_tab[RelocCount] = Lba;
RelocCount++;
if(RelocCount >= Cache->WriteCount) {
Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount);
break;
}
}
}
} // end WCacheSyncReloc__()
/*
WCacheDiscardBlocks__() removes specified blocks from cache.
Blocks are not flushed to media.
Public routine
*/
VOID
WCacheDiscardBlocks__(
IN PW_CACHE Cache,
IN PVOID Context,
IN lba_t ReqLba,
IN ULONG BCount
)
{
ULONG frame;
lba_t firstLba;
lba_t* List;
lba_t Lba;
PW_CACHE_ENTRY block_array;
BOOLEAN mod;
ULONG i;
ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
UDFPrint((" Discard req: %x@%x\n",BCount, ReqLba));
List = Cache->CachedBlocksList;
if(!List) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
return;
}
i = WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba);
// enumerate requested blocks
while((List[i] < (ReqLba+BCount)) && (i < Cache->BlockCount)) {
Lba = List[i];
frame = Lba >> Cache->BlocksPerFrameSh;
firstLba = frame << Cache->BlocksPerFrameSh;
block_array = Cache->FrameList[frame].Frame;
if(!block_array) {
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
BrutePoint();
return;
}
// check if modified
mod = WCacheGetModFlag(block_array, Lba - firstLba);
// just discard
// mark as non-cached & free pool
if(WCacheSectorAddr(block_array,Lba-firstLba)) {
WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
if(mod)
WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
// mark as non-cached & free pool
WCacheFreeSector(frame, Lba-firstLba);
// check if frame is empty
if(!Cache->FrameList[frame].BlockCount) {
WCacheRemoveFrame(Cache, Context, frame);
} else {
ASSERT(Cache->FrameList[frame].Frame);
}
} else {
// we should never get here !!!
// getting this part of code means that we have
// placed non-cached block in CachedBlocksList
BrutePoint();
}
}
ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
} // end WCacheDiscardBlocks__()
OSSTATUS
WCacheCompleteAsync__(
IN PVOID WContext,
IN OSSTATUS Status
)
{
PW_CACHE_ASYNC AsyncCtx = (PW_CACHE_ASYNC)WContext;
// PW_CACHE Cache = AsyncCtx->Cache;
AsyncCtx->PhContext.IosbToUse.Status = Status;
KeSetEvent(&(AsyncCtx->PhContext.event), 0, FALSE);
return STATUS_SUCCESS;
} // end WCacheSetMode__()
/*
WCacheDecodeFlags() updates internal BOOLEANs according to Flags
Internal routine
*/
OSSTATUS
__fastcall
WCacheDecodeFlags(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN ULONG Flags // cache mode flags
)
{
//ULONG OldFlags;
if(Flags & ~WCACHE_VALID_FLAGS) {
UDFPrint(("Invalid flags: %x\n", Flags & ~WCACHE_VALID_FLAGS));
return STATUS_INVALID_PARAMETER;
}
Cache->CacheWholePacket = (Flags & WCACHE_CACHE_WHOLE_PACKET) ? TRUE : FALSE;
Cache->DoNotCompare = (Flags & WCACHE_DO_NOT_COMPARE) ? TRUE : FALSE;
Cache->Chained = (Flags & WCACHE_CHAINED_IO) ? TRUE : FALSE;
Cache->RememberBB = (Flags & WCACHE_MARK_BAD_BLOCKS) ? TRUE : FALSE;
if(Cache->RememberBB) {
Cache->NoWriteBB = (Flags & WCACHE_RO_BAD_BLOCKS) ? TRUE : FALSE;
}
Cache->NoWriteThrough = (Flags & WCACHE_NO_WRITE_THROUGH) ? TRUE : FALSE;
Cache->Flags = Flags;
return STATUS_SUCCESS;
}
/*
WCacheChFlags__() changes cache flags.
Public routine
*/
ULONG
WCacheChFlags__(
IN PW_CACHE Cache, // pointer to the Cache Control structure
IN ULONG SetFlags, // cache mode/media type to be set
IN ULONG ClrFlags // cache mode/media type to be cleared
)
{
ULONG Flags;
if(SetFlags || ClrFlags) {
Flags = (Cache->Flags & ~ClrFlags) | SetFlags;
if(!OS_SUCCESS(WCacheDecodeFlags(Cache, Flags))) {
return -1;
}
} else {
return Cache->Flags;
}
return Flags;
} // end WCacheSetMode__()