mirror of
https://github.com/reactos/reactos.git
synced 2024-11-05 22:26:39 +00:00
3652 lines
124 KiB
C++
3652 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;
|
|
ULONG 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 ULONG 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)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)(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 PULONG 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;
|
|
ULONG 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;
|
|
ULONG _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;
|
|
ULONG _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 PULONG 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;
|
|
ULONG BS = Cache->BlockSize;
|
|
PCHAR addr;
|
|
ULONG to_read, saved_to_read;
|
|
// PCHAR saved_buff = Buffer;
|
|
ULONG _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 PULONG 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;
|
|
ULONG _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;
|
|
ULONG 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;
|
|
ULONG 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->CachedBlocksList)
|
|
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;
|
|
ULONG 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;
|
|
ULONG _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;
|
|
ULONG 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;
|
|
ULONG 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__()
|