CcOperationZero
} CC_COPY_OPERATION;
+typedef enum _CC_CAN_WRITE_RETRY
+{
+ FirstTry = 0,
+ RetryAllowRemote = 253,
+ RetryForceCheckPerFile = 254,
+ RetryMasterLocked = 255,
+} CC_CAN_WRITE_RETRY;
+
ULONG CcRosTraceLevel = 0;
ULONG CcFastMdlReadWait;
ULONG CcFastMdlReadNotPossible;
ULONG PartialLength;
PVOID BaseAddress;
BOOLEAN Valid;
+ PPRIVATE_CACHE_MAP PrivateCacheMap;
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+ PrivateCacheMap = FileObject->PrivateCacheMap;
CurrentOffset = FileOffset;
BytesCopied = 0;
if (Operation != CcOperationZero)
Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
}
+
+ /* If that was a successful sync read operation, let's handle read ahead */
+ if (Operation == CcOperationRead && Length == 0 && Wait)
+ {
+ /* If file isn't random access, schedule next read */
+ if (!BooleanFlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
+ {
+ CcScheduleReadAhead(FileObject, (PLARGE_INTEGER)&FileOffset, BytesCopied);
+ }
+
+ /* And update read history in private cache map */
+ PrivateCacheMap->FileOffset1.QuadPart = PrivateCacheMap->FileOffset2.QuadPart;
+ PrivateCacheMap->BeyondLastByte1.QuadPart = PrivateCacheMap->BeyondLastByte2.QuadPart;
+ PrivateCacheMap->FileOffset2.QuadPart = FileOffset;
+ PrivateCacheMap->BeyondLastByte2.QuadPart = FileOffset + BytesCopied;
+ }
+
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = BytesCopied;
return TRUE;
}
/* Check we can write */
- if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, TRUE))
+ if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, RetryForceCheckPerFile))
{
/* We can, so remove it from the list and stop looking for entry */
RemoveEntryList(&DeferredWrite->DeferredWriteLinks);
}
}
+VOID
+CcPerformReadAhead(
+ IN PFILE_OBJECT FileObject)
+{
+ NTSTATUS Status;
+ LONGLONG CurrentOffset;
+ KIRQL OldIrql;
+ PROS_SHARED_CACHE_MAP SharedCacheMap;
+ PROS_VACB Vacb;
+ ULONG PartialLength;
+ PVOID BaseAddress;
+ BOOLEAN Valid;
+ ULONG Length;
+ PPRIVATE_CACHE_MAP PrivateCacheMap;
+ BOOLEAN Locked;
+
+ SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+
+ /* Critical:
+ * PrivateCacheMap might disappear in-between if the handle
+ * to the file is closed (private is attached to the handle not to
+ * the file), so we need to lock the master lock while we deal with
+ * it. It won't disappear without attempting to lock such lock.
+ */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+ PrivateCacheMap = FileObject->PrivateCacheMap;
+ /* If the handle was closed since the read ahead was scheduled, just quit */
+ if (PrivateCacheMap == NULL)
+ {
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+ ObDereferenceObject(FileObject);
+ return;
+ }
+ /* Otherwise, extract read offset and length and release private map */
+ else
+ {
+ KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+ CurrentOffset = PrivateCacheMap->ReadAheadOffset[1].QuadPart;
+ Length = PrivateCacheMap->ReadAheadLength[1];
+ KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+ }
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+
+ /* Time to go! */
+ DPRINT("Doing ReadAhead for %p\n", FileObject);
+ /* Lock the file, first */
+ if (!SharedCacheMap->Callbacks->AcquireForReadAhead(SharedCacheMap->LazyWriteContext, FALSE))
+ {
+ Locked = FALSE;
+ goto Clear;
+ }
+
+ /* Remember it's locked */
+ Locked = TRUE;
+
+ /* Next of the algorithm will lock like CcCopyData with the slight
+ * difference that we don't copy data back to an user-backed buffer
+ * We just bring data into Cc
+ */
+ PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY;
+ if (PartialLength != 0)
+ {
+ PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
+ Status = CcRosRequestVacb(SharedCacheMap,
+ ROUND_DOWN(CurrentOffset,
+ VACB_MAPPING_GRANULARITY),
+ &BaseAddress,
+ &Valid,
+ &Vacb);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to request VACB: %lx!\n", Status);
+ goto Clear;
+ }
+
+ if (!Valid)
+ {
+ Status = CcReadVirtualAddress(Vacb);
+ if (!NT_SUCCESS(Status))
+ {
+ CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
+ DPRINT1("Failed to read data: %lx!\n", Status);
+ goto Clear;
+ }
+ }
+
+ CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
+
+ Length -= PartialLength;
+ CurrentOffset += PartialLength;
+ }
+
+ while (Length > 0)
+ {
+ ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0);
+ PartialLength = min(VACB_MAPPING_GRANULARITY, Length);
+ Status = CcRosRequestVacb(SharedCacheMap,
+ CurrentOffset,
+ &BaseAddress,
+ &Valid,
+ &Vacb);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to request VACB: %lx!\n", Status);
+ goto Clear;
+ }
+
+ if (!Valid)
+ {
+ Status = CcReadVirtualAddress(Vacb);
+ if (!NT_SUCCESS(Status))
+ {
+ CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
+ DPRINT1("Failed to read data: %lx!\n", Status);
+ goto Clear;
+ }
+ }
+
+ CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
+
+ Length -= PartialLength;
+ CurrentOffset += PartialLength;
+ }
+
+Clear:
+ /* See previous comment about private cache map */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+ PrivateCacheMap = FileObject->PrivateCacheMap;
+ if (PrivateCacheMap != NULL)
+ {
+ /* Mark read ahead as unactive */
+ KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+ InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, 0xFFFEFFFF);
+ KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+ }
+ KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
+
+ /* If file was locked, release it */
+ if (Locked)
+ {
+ SharedCacheMap->Callbacks->ReleaseFromReadAhead(SharedCacheMap->LazyWriteContext);
+ }
+
+ /* And drop our extra reference (See: CcScheduleReadAhead) */
+ ObDereferenceObject(FileObject);
+
+ return;
+}
+
/*
* @unimplemented
*/
IN BOOLEAN Wait,
IN BOOLEAN Retrying)
{
+ KIRQL OldIrql;
KEVENT WaitEvent;
+ ULONG Length, Pages;
+ BOOLEAN PerFileDefer;
DEFERRED_WRITE Context;
PFSRTL_COMMON_FCB_HEADER Fcb;
+ CC_CAN_WRITE_RETRY TryContext;
PROS_SHARED_CACHE_MAP SharedCacheMap;
CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n",
FileObject, BytesToWrite, Wait, Retrying);
- /* We cannot write if dirty pages count is above threshold */
- if (CcTotalDirtyPages > CcDirtyPageThreshold)
+ /* Write through is always OK */
+ if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH))
{
- /* Can the caller wait till it's possible to write? */
- goto CanIWait;
+ return TRUE;
}
- /* We cannot write if dirty pages count will bring use above
- * XXX: Might not be accurate
- */
- if (CcTotalDirtyPages + (BytesToWrite / PAGE_SIZE) > CcDirtyPageThreshold)
+ TryContext = Retrying;
+ /* Allow remote file if not from posted */
+ if (IoIsFileOriginRemote(FileObject) && TryContext < RetryAllowRemote)
{
- /* Can the caller wait till it's possible to write? */
- goto CanIWait;
+ return TRUE;
}
- /* Is there a limit per file object? */
- Fcb = FileObject->FsContext;
- SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
- if (!BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES) ||
- SharedCacheMap->DirtyPageThreshold == 0)
+ /* Don't exceed max tolerated size */
+ Length = MAX_ZERO_LENGTH;
+ if (BytesToWrite < MAX_ZERO_LENGTH)
{
- /* Nope, so that's fine, allow write operation */
- return TRUE;
+ Length = BytesToWrite;
}
- /* Is dirty page count above local threshold? */
- if (SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
+ /* Convert it to pages count */
+ Pages = (Length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ /* By default, assume limits per file won't be hit */
+ PerFileDefer = FALSE;
+ Fcb = FileObject->FsContext;
+ /* Do we have to check for limits per file? */
+ if (TryContext >= RetryForceCheckPerFile ||
+ BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES))
{
- /* Can the caller wait till it's possible to write? */
- goto CanIWait;
+ /* If master is not locked, lock it now */
+ if (TryContext != RetryMasterLocked)
+ {
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+ }
+
+ /* Let's not assume the file is cached... */
+ if (FileObject->SectionObjectPointer != NULL &&
+ FileObject->SectionObjectPointer->SharedCacheMap != NULL)
+ {
+ SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+ /* Do we have limits per file set? */
+ if (SharedCacheMap->DirtyPageThreshold != 0 &&
+ SharedCacheMap->DirtyPages != 0)
+ {
+ /* Yes, check whether they are blocking */
+ if (Pages + SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
+ {
+ PerFileDefer = TRUE;
+ }
+ }
+ }
+
+ /* And don't forget to release master */
+ if (TryContext != RetryMasterLocked)
+ {
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+ }
}
- /* We cannot write if dirty pages count will bring use above
- * XXX: Might not be accurate
+ /* So, now allow write if:
+ * - Not the first try or we have no throttling yet
+ * AND:
+ * - We don't execeed threshold!
*/
- if (SharedCacheMap->DirtyPages + (BytesToWrite / PAGE_SIZE) > SharedCacheMap->DirtyPageThreshold)
+ if ((TryContext != FirstTry || IsListEmpty(&CcDeferredWrites)) &&
+ CcTotalDirtyPages + Pages < CcDirtyPageThreshold &&
+ !PerFileDefer)
{
- /* Can the caller wait till it's possible to write? */
- goto CanIWait;
+ return TRUE;
}
- return TRUE;
-
-CanIWait:
- /* If we reached that point, it means caller cannot write
- * If he cannot wait, then fail and deny write
+ /* If we can wait, we'll start the wait loop for waiting till we can
+ * write for real
*/
if (!Wait)
{
IN ULONG BytesToWrite,
IN BOOLEAN Retrying)
{
+ KIRQL OldIrql;
PDEFERRED_WRITE Context;
+ PFSRTL_COMMON_FCB_HEADER Fcb;
CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n",
FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying);
return;
}
+ Fcb = FileObject->FsContext;
+
/* Otherwise, initialize the context */
RtlZeroMemory(Context, sizeof(DEFERRED_WRITE));
Context->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
Context->Context1 = Context1;
Context->Context2 = Context2;
Context->BytesToWrite = BytesToWrite;
+ Context->LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
/* And queue it */
if (Retrying)
/* Try to execute the posted writes */
CcPostDeferredWrites();
- /* FIXME: lock master */
+ /* Schedule a lazy writer run to handle deferred writes */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
if (!LazyWriter.ScanActive)
{
CcScheduleLazyWriteScan(FALSE);
}
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
}
/*