1202 lines
31 KiB
C
1202 lines
31 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
prefboot.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code for boot prefetching.
|
|
|
|
Author:
|
|
|
|
Cenk Ergan (cenke) 15-Mar-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cc.h"
|
|
#include "zwapi.h"
|
|
#include "prefetch.h"
|
|
#include "preftchp.h"
|
|
#include "stdio.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, CcPfBeginBootPhase)
|
|
#pragma alloc_text(PAGE, CcPfBootWorker)
|
|
#pragma alloc_text(PAGE, CcPfBootQueueEndTraceTimer)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
//
|
|
// Globals:
|
|
//
|
|
|
|
//
|
|
// Whether the system is currently prefetching for boot.
|
|
//
|
|
|
|
LOGICAL CcPfPrefetchingForBoot = FALSE;
|
|
|
|
//
|
|
// Current boot phase, only updated in begin boot phase routine.
|
|
//
|
|
|
|
PF_BOOT_PHASE_ID CcPfBootPhase = 0;
|
|
|
|
//
|
|
// Prefetcher globals.
|
|
//
|
|
|
|
extern CCPF_PREFETCHER_GLOBALS CcPfGlobals;
|
|
|
|
//
|
|
// Routines for boot prefetching.
|
|
//
|
|
|
|
NTSTATUS
|
|
CcPfBeginBootPhase(
|
|
PF_BOOT_PHASE_ID Phase
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the control center for the boot prefetcher.
|
|
It is called to notify boot prefetcher of boot progress.
|
|
|
|
Arguments:
|
|
|
|
Phase - Boot phase the system is entering.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
Environment:
|
|
|
|
Kernel mode. IRQL == PASSIVE_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER VideoInitEndTime;
|
|
LARGE_INTEGER MaxWaitTime;
|
|
LONGLONG VideoInitTimeIn100ns;
|
|
HANDLE ThreadHandle;
|
|
PETHREAD Thread;
|
|
PERFINFO_BOOT_PHASE_START LogEntry;
|
|
PF_BOOT_PHASE_ID OriginalPhase;
|
|
PF_BOOT_PHASE_ID NewPhase;
|
|
ULONG VideoInitTime;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// This is the boot prefetcher. It is allocated and free'd in this routine.
|
|
// It is passed to the spawned boot worker if boot prefetching is enabled.
|
|
//
|
|
|
|
static PCCPF_BOOT_PREFETCHER BootPrefetcher = NULL;
|
|
|
|
//
|
|
// This is the system time when we started initializing the video.
|
|
//
|
|
|
|
static LARGE_INTEGER VideoInitStartTime;
|
|
|
|
DBGPR((CCPFID,PFTRC,"CCPF: BeginBootPhase(%d)\n", (ULONG)Phase));
|
|
|
|
//
|
|
// Make sure phase is valid.
|
|
//
|
|
|
|
if (Phase >= PfMaxBootPhaseId) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Log phase to trace buffer.
|
|
//
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_LOADER)) {
|
|
|
|
LogEntry.Phase = Phase;
|
|
|
|
PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PHASE_START,
|
|
&LogEntry,
|
|
sizeof(LogEntry));
|
|
}
|
|
|
|
//
|
|
// Update the global current boot phase.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
OriginalPhase = CcPfBootPhase;
|
|
|
|
if (Phase <= OriginalPhase) {
|
|
Status = STATUS_TOO_LATE;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// If CcPfBootPhase is still OriginalPhase, set it to Phase.
|
|
//
|
|
|
|
NewPhase = InterlockedCompareExchange(&(LONG)CcPfBootPhase, Phase, OriginalPhase);
|
|
|
|
if (NewPhase == OriginalPhase) {
|
|
|
|
//
|
|
// CcPfBootPhase was still OriginalPhase, so now it is set to
|
|
// Phase. We are done.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Perform the work we have to do for this boot phase.
|
|
//
|
|
|
|
switch (Phase) {
|
|
|
|
case PfSystemDriverInitPhase:
|
|
|
|
//
|
|
// Update whether prefetcher is enabled or not.
|
|
//
|
|
|
|
CcPfDetermineEnablePrefetcher();
|
|
|
|
//
|
|
// If boot prefetching is not enabled, we are done.
|
|
//
|
|
|
|
if (!CCPF_IS_PREFETCHER_ENABLED() ||
|
|
CcPfGlobals.Parameters.Parameters.EnableStatus[PfSystemBootScenarioType] != PfSvEnabled) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate and initialize boot prefetcher.
|
|
//
|
|
|
|
BootPrefetcher = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(*BootPrefetcher),
|
|
CCPF_ALLOC_BOOTWRKR_TAG);
|
|
|
|
if (!BootPrefetcher) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
KeInitializeEvent(&BootPrefetcher->SystemDriversPrefetchingDone,
|
|
NotificationEvent,
|
|
FALSE);
|
|
KeInitializeEvent(&BootPrefetcher->PreSmssPrefetchingDone,
|
|
NotificationEvent,
|
|
FALSE);
|
|
KeInitializeEvent(&BootPrefetcher->VideoInitPrefetchingDone,
|
|
NotificationEvent,
|
|
FALSE);
|
|
KeInitializeEvent(&BootPrefetcher->VideoInitStarted,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Kick off the boot worker in paralel.
|
|
//
|
|
|
|
Status = PsCreateSystemThread(&ThreadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
CcPfBootWorker,
|
|
BootPrefetcher);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Give boot worker some head start by bumping its
|
|
// priority. This helps to make sure pages we will
|
|
// prefetch are put into transition before boot gets
|
|
// ahead of the prefetcher.
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
KernelMode,
|
|
&Thread,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
KeSetPriorityThread(&Thread->Tcb, HIGH_PRIORITY - 1);
|
|
ObDereferenceObject(Thread);
|
|
}
|
|
|
|
ZwClose(ThreadHandle);
|
|
|
|
//
|
|
// Before returning to initialize system drivers, wait
|
|
// for boot worker to make progress.
|
|
//
|
|
|
|
KeWaitForSingleObject(&BootPrefetcher->SystemDriversPrefetchingDone,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Free the allocated boot prefetcher.
|
|
//
|
|
|
|
ExFreePool(BootPrefetcher);
|
|
BootPrefetcher = NULL;
|
|
}
|
|
|
|
break;
|
|
|
|
case PfSessionManagerInitPhase:
|
|
|
|
//
|
|
// Wait for boot worker to make enough progress before launching
|
|
// session manager.
|
|
//
|
|
|
|
if (BootPrefetcher) {
|
|
KeWaitForSingleObject(&BootPrefetcher->PreSmssPrefetchingDone,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
case PfVideoInitPhase:
|
|
|
|
//
|
|
// Note when video initialization started.
|
|
//
|
|
|
|
KeQuerySystemTime(&VideoInitStartTime);
|
|
|
|
//
|
|
// Signal boot prefetcher to start prefetching in parallel to video
|
|
// initialization.
|
|
//
|
|
|
|
if (BootPrefetcher) {
|
|
KeSetEvent(&BootPrefetcher->VideoInitStarted,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
case PfPostVideoInitPhase:
|
|
|
|
//
|
|
// Note when we complete video initialization. Save how long video
|
|
// initialization took in the registry in milliseconds.
|
|
//
|
|
|
|
KeQuerySystemTime(&VideoInitEndTime);
|
|
|
|
VideoInitTimeIn100ns = VideoInitEndTime.QuadPart - VideoInitStartTime.QuadPart;
|
|
VideoInitTime = (ULONG) (VideoInitTimeIn100ns / (1i64 * 10 * 1000));
|
|
|
|
KeEnterCriticalRegionThread(KeGetCurrentThread());
|
|
ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
|
|
|
|
Status = CcPfSetParameter(CcPfGlobals.Parameters.ParametersKey,
|
|
CCPF_VIDEO_INIT_TIME_VALUE_NAME,
|
|
REG_DWORD,
|
|
&VideoInitTime,
|
|
sizeof(VideoInitTime));
|
|
|
|
ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
|
|
KeLeaveCriticalRegionThread(KeGetCurrentThread());
|
|
|
|
//
|
|
// Wait for prefetching parallel to video initialization to complete.
|
|
//
|
|
|
|
if (BootPrefetcher) {
|
|
KeWaitForSingleObject(&BootPrefetcher->VideoInitPrefetchingDone,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
case PfBootAcceptedRegistryInitPhase:
|
|
|
|
//
|
|
// Service Controller has accepted this boot as a valid boot.
|
|
// Boot & system services have initialized successfully.
|
|
//
|
|
|
|
//
|
|
// We are done with boot prefetching. No one else could be accessing
|
|
// BootPrefetcher structure at this point.
|
|
//
|
|
|
|
if (BootPrefetcher) {
|
|
|
|
//
|
|
// Cleanup the allocated boot prefetcher.
|
|
//
|
|
|
|
ExFreePool(BootPrefetcher);
|
|
BootPrefetcher = NULL;
|
|
|
|
//
|
|
// Determine if the prefetcher is enabled now that boot
|
|
// is over.
|
|
//
|
|
|
|
CcPfDetermineEnablePrefetcher();
|
|
}
|
|
|
|
//
|
|
// The user may not log in after booting.
|
|
// Queue a timer to end boot trace.
|
|
//
|
|
|
|
MaxWaitTime.QuadPart = -1i64 * 60 * 1000 * 1000 * 10; // 60 seconds.
|
|
|
|
CcPfBootQueueEndTraceTimer(&MaxWaitTime);
|
|
|
|
break;
|
|
|
|
case PfUserShellReadyPhase:
|
|
|
|
//
|
|
// Explorer has started, but start menu items may still be launching.
|
|
// Queue a timer to end boot trace.
|
|
//
|
|
|
|
MaxWaitTime.QuadPart = -1i64 * 30 * 1000 * 1000 * 10; // 30 seconds.
|
|
|
|
CcPfBootQueueEndTraceTimer(&MaxWaitTime);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Ignored for now.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Fall through with status from switch statement.
|
|
//
|
|
|
|
cleanup:
|
|
|
|
DBGPR((CCPFID,PFTRC,"CCPF: BeginBootPhase(%d)=%x\n", (ULONG)Phase, Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CcPfBootWorker(
|
|
PCCPF_BOOT_PREFETCHER BootPrefetcher
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is queued to prefetch and start tracing boot in parallel.
|
|
|
|
Arguments:
|
|
|
|
BootPrefetcher - Pointer to boot prefetcher context.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode. IRQL == PASSIVE_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PF_SCENARIO_ID BootScenarioId;
|
|
CCPF_PREFETCH_HEADER PrefetchHeader;
|
|
CCPF_BASIC_SCENARIO_INFORMATION ScenarioInfo;
|
|
CCPF_BOOT_SCENARIO_INFORMATION BootScenarioInfo;
|
|
PERFINFO_BOOT_PREFETCH_INFORMATION LogEntry;
|
|
ULONG NumPages;
|
|
ULONG RequiredSize;
|
|
ULONG NumPagesPrefetched;
|
|
ULONG TotalPagesPrefetched;
|
|
ULONG BootPrefetchAdjustment;
|
|
ULONG AvailablePages;
|
|
ULONG NumPagesToPrefetch;
|
|
ULONG TotalPagesToPrefetch;
|
|
ULONG RemainingDataPages;
|
|
ULONG RemainingImagePages;
|
|
ULONG VideoInitTime;
|
|
ULONG VideoInitPagesPerSecond;
|
|
ULONG VideoInitMaxPages;
|
|
ULONG RemainingVideoInitPages;
|
|
ULONG VideoInitDataPages;
|
|
ULONG VideoInitImagePages;
|
|
ULONG PrefetchPhaseIdx;
|
|
ULONG LastPrefetchPhaseIdx;
|
|
ULONG SystemDriverPrefetchingPhaseIdx;
|
|
ULONG PreSmssPrefetchingPhaseIdx;
|
|
ULONG VideoInitPrefetchingPhaseIdx;
|
|
ULONG ValueSize;
|
|
CCPF_BOOT_SCENARIO_PHASE BootPhaseIdx;
|
|
NTSTATUS Status;
|
|
BOOLEAN OutOfAvailablePages;
|
|
|
|
//
|
|
// First we will prefetch data pages, then image pages.
|
|
//
|
|
|
|
enum {
|
|
DataCursor = 0,
|
|
ImageCursor,
|
|
MaxCursor
|
|
} CursorIdx;
|
|
|
|
CCPF_BOOT_PREFETCH_CURSOR Cursors[MaxCursor];
|
|
PCCPF_BOOT_PREFETCH_CURSOR Cursor;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
CcPfInitializePrefetchHeader(&PrefetchHeader);
|
|
TotalPagesPrefetched = 0;
|
|
OutOfAvailablePages = FALSE;
|
|
|
|
DBGPR((CCPFID,PFTRC,"CCPF: BootWorker()\n"));
|
|
|
|
//
|
|
// Initialize boot scenario ID.
|
|
//
|
|
|
|
wcsncpy(BootScenarioId.ScenName,
|
|
PF_BOOT_SCENARIO_NAME,
|
|
PF_SCEN_ID_MAX_CHARS);
|
|
|
|
BootScenarioId.ScenName[PF_SCEN_ID_MAX_CHARS] = 0;
|
|
BootScenarioId.HashId = PF_BOOT_SCENARIO_HASHID;
|
|
|
|
//
|
|
// Start boot prefetch tracing.
|
|
//
|
|
|
|
CcPfBeginTrace(&BootScenarioId, PfSystemBootScenarioType, PsInitialSystemProcess);
|
|
|
|
//
|
|
// If we try to prefetch more pages then what we have available, we will
|
|
// end up cannibalizing the pages we prefetched into the standby list.
|
|
// To avoid cannibalizing, we check MmAvailablePages but leave some
|
|
// breathing room for metadata pages, allocations from the driver
|
|
// initialization phase etc.
|
|
//
|
|
|
|
BootPrefetchAdjustment = 512;
|
|
|
|
//
|
|
// We also know that right after we prefetch for boot, in smss when
|
|
// initializing the registry we'll use up 8-10MB of prefetched pages if we
|
|
// don't have anything left in the free list. So we leave some room for
|
|
// that too.
|
|
//
|
|
|
|
BootPrefetchAdjustment += 8 * 1024 * 1024 / PAGE_SIZE;
|
|
|
|
//
|
|
// Get prefetch instructions.
|
|
//
|
|
|
|
Status = CcPfGetPrefetchInstructions(&BootScenarioId,
|
|
PfSystemBootScenarioType,
|
|
&PrefetchHeader.Scenario);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Query the total number of pages to be prefetched.
|
|
//
|
|
|
|
Status = CcPfQueryScenarioInformation(PrefetchHeader.Scenario,
|
|
CcPfBasicScenarioInformation,
|
|
&ScenarioInfo,
|
|
sizeof(ScenarioInfo),
|
|
&RequiredSize);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Query the number of pages we have to prefetch for boot phases.
|
|
//
|
|
|
|
|
|
Status = CcPfQueryScenarioInformation(PrefetchHeader.Scenario,
|
|
CcPfBootScenarioInformation,
|
|
&BootScenarioInfo,
|
|
sizeof(BootScenarioInfo),
|
|
&RequiredSize);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Read how long it took to initialize video in the last boot.
|
|
//
|
|
|
|
KeEnterCriticalRegionThread(KeGetCurrentThread());
|
|
ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
|
|
|
|
ValueSize = sizeof(VideoInitTime);
|
|
Status = CcPfGetParameter(CcPfGlobals.Parameters.ParametersKey,
|
|
CCPF_VIDEO_INIT_TIME_VALUE_NAME,
|
|
REG_DWORD,
|
|
&VideoInitTime,
|
|
&ValueSize);
|
|
|
|
ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
|
|
KeLeaveCriticalRegionThread(KeGetCurrentThread());
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Reset video init time, so we don't attempt to prefetch
|
|
// in parallel to it.
|
|
//
|
|
|
|
VideoInitTime = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Verify the value we read from registry.
|
|
//
|
|
|
|
if (VideoInitTime > CCPF_MAX_VIDEO_INIT_TIME) {
|
|
VideoInitTime = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read how many pages per second we should be trying to prefetching
|
|
// in parallel to video initialization.
|
|
//
|
|
|
|
KeEnterCriticalRegionThread(KeGetCurrentThread());
|
|
ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
|
|
|
|
ValueSize = sizeof(VideoInitPagesPerSecond);
|
|
Status = CcPfGetParameter(CcPfGlobals.Parameters.ParametersKey,
|
|
CCPF_VIDEO_INIT_PAGES_PER_SECOND_VALUE_NAME,
|
|
REG_DWORD,
|
|
&VideoInitPagesPerSecond,
|
|
&ValueSize);
|
|
|
|
ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
|
|
KeLeaveCriticalRegionThread(KeGetCurrentThread());
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// There was no valid value in the registry. Use the default.
|
|
//
|
|
|
|
VideoInitPagesPerSecond = CCPF_VIDEO_INIT_DEFAULT_PAGES_PER_SECOND;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Verify the value we read from registry.
|
|
//
|
|
|
|
if (VideoInitPagesPerSecond > CCPF_VIDEO_INIT_MAX_PAGES_PER_SECOND) {
|
|
VideoInitPagesPerSecond = CCPF_VIDEO_INIT_MAX_PAGES_PER_SECOND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine how many pages max we can prefetch in parallel to video
|
|
// initialization.
|
|
//
|
|
|
|
VideoInitMaxPages = (VideoInitTime / 1000) * VideoInitPagesPerSecond;
|
|
|
|
//
|
|
// We can only prefetch pages used after winlogon in parallel to video
|
|
// initialization. Determine exactly how many pages we will prefetch
|
|
// starting from the last boot phase.
|
|
//
|
|
|
|
RemainingVideoInitPages = VideoInitMaxPages;
|
|
VideoInitDataPages = 0;
|
|
VideoInitImagePages = 0;
|
|
|
|
for (BootPhaseIdx = CcPfBootScenMaxPhase - 1;
|
|
RemainingVideoInitPages && (BootPhaseIdx >= CcPfBootScenSystemProcInitPhase);
|
|
BootPhaseIdx--) {
|
|
|
|
NumPages = CCPF_MIN(RemainingVideoInitPages, BootScenarioInfo.NumImagePages[BootPhaseIdx]);
|
|
VideoInitImagePages += NumPages;
|
|
RemainingVideoInitPages -= NumPages;
|
|
|
|
if (RemainingVideoInitPages) {
|
|
NumPages = CCPF_MIN(RemainingVideoInitPages, BootScenarioInfo.NumDataPages[BootPhaseIdx]);
|
|
VideoInitDataPages += NumPages;
|
|
RemainingVideoInitPages -= NumPages;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Let MM know that we have started prefetching for boot.
|
|
//
|
|
|
|
CcPfPrefetchingForBoot = TRUE;
|
|
|
|
//
|
|
// Log that we are starting prefetch disk I/Os.
|
|
//
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_DISK_IO)) {
|
|
|
|
LogEntry.Action = 0;
|
|
LogEntry.Status = 0;
|
|
LogEntry.Pages = ScenarioInfo.NumDataPages + ScenarioInfo.NumImagePages;
|
|
|
|
PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PREFETCH_INFORMATION,
|
|
&LogEntry,
|
|
sizeof(LogEntry));
|
|
}
|
|
|
|
//
|
|
// Verify & open the volumes that we will prefetch from.
|
|
//
|
|
|
|
Status = CcPfOpenVolumesForPrefetch(&PrefetchHeader);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Prefetch the metadata.
|
|
//
|
|
|
|
CcPfPrefetchMetadata(&PrefetchHeader);
|
|
|
|
//
|
|
// Initialize the boot prefetch cursors for data and image.
|
|
//
|
|
|
|
RtlZeroMemory(Cursors, sizeof(Cursors));
|
|
|
|
Cursors[DataCursor].PrefetchType = CcPfPrefetchPartOfDataPages;
|
|
Cursors[ImageCursor].PrefetchType = CcPfPrefetchPartOfImagePages;
|
|
|
|
PrefetchPhaseIdx = 0;
|
|
RemainingDataPages = ScenarioInfo.NumDataPages;
|
|
RemainingImagePages = ScenarioInfo.NumImagePages;
|
|
|
|
//
|
|
// Setup the cursors for phases in which we will prefetch for boot.
|
|
// First we will prefetch for system drivers.
|
|
//
|
|
|
|
NumPages = BootScenarioInfo.NumDataPages[CcPfBootScenDriverInitPhase];
|
|
Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
|
|
RemainingDataPages -= NumPages;
|
|
|
|
NumPages = BootScenarioInfo.NumImagePages[CcPfBootScenDriverInitPhase];
|
|
Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
|
|
RemainingImagePages -= NumPages;
|
|
|
|
SystemDriverPrefetchingPhaseIdx = PrefetchPhaseIdx;
|
|
|
|
PrefetchPhaseIdx++;
|
|
|
|
//
|
|
// Account for the video init pages we will prefetch last.
|
|
//
|
|
|
|
RemainingDataPages -= VideoInitDataPages;
|
|
RemainingImagePages -= VideoInitImagePages;
|
|
|
|
//
|
|
// If we have plenty of available memory, prefetch the rest of the pages
|
|
// (i.e. left over after driver init pages) in one pass.
|
|
//
|
|
|
|
TotalPagesToPrefetch = ScenarioInfo.NumDataPages + ScenarioInfo.NumImagePages;
|
|
|
|
if (MmAvailablePages > BootPrefetchAdjustment + TotalPagesToPrefetch) {
|
|
|
|
Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = RemainingDataPages;
|
|
RemainingDataPages = 0;
|
|
|
|
Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = RemainingImagePages;
|
|
RemainingImagePages = 0;
|
|
|
|
PrefetchPhaseIdx++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We will be short on memory. Try to prefetch for as many phases of
|
|
// boot as we can in parallel. Prefetching data & image pages per boot
|
|
// phase, so we don't end up with data pages for all phase but no image
|
|
// pages so we have to go to the disk in each phase. Prefetching in
|
|
// chunks also help that all the pages we need for the initial phases
|
|
// of boot ending up at the end of the standby list, since when
|
|
// CcPfPrefetchingForBoot is set, prefetched pages will be inserted
|
|
// from the front of the standby list.
|
|
//
|
|
|
|
for (BootPhaseIdx = CcPfBootScenDriverInitPhase + 1;
|
|
BootPhaseIdx < CcPfBootScenMaxPhase;
|
|
BootPhaseIdx++) {
|
|
|
|
//
|
|
// If we don't have any type of pages left to prefetch, we are done.
|
|
//
|
|
|
|
if (!RemainingDataPages && !RemainingImagePages) {
|
|
break;
|
|
}
|
|
|
|
NumPages = CCPF_MIN(RemainingDataPages, BootScenarioInfo.NumDataPages[BootPhaseIdx]);
|
|
RemainingDataPages -= NumPages;
|
|
Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
|
|
|
|
NumPages = CCPF_MIN(RemainingImagePages, BootScenarioInfo.NumImagePages[BootPhaseIdx]);
|
|
RemainingImagePages -= NumPages;
|
|
Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
|
|
|
|
PrefetchPhaseIdx++;
|
|
}
|
|
}
|
|
|
|
PreSmssPrefetchingPhaseIdx = PrefetchPhaseIdx - 1;
|
|
|
|
//
|
|
// If we'll be prefetching pages in parallel to video initialization, now
|
|
// add the phase for it.
|
|
//
|
|
|
|
if (VideoInitDataPages || VideoInitImagePages) {
|
|
|
|
Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = VideoInitDataPages;
|
|
Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = VideoInitImagePages;
|
|
|
|
VideoInitPrefetchingPhaseIdx = PrefetchPhaseIdx;
|
|
|
|
PrefetchPhaseIdx++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We won't have a prefetching phase parallel to video initialization.
|
|
//
|
|
|
|
VideoInitPrefetchingPhaseIdx = CCPF_MAX_BOOT_PREFETCH_PHASES;
|
|
}
|
|
|
|
//
|
|
// We should not end up with more prefetch phases than we have room for.
|
|
//
|
|
|
|
CCPF_ASSERT(PrefetchPhaseIdx < CCPF_MAX_BOOT_PREFETCH_PHASES);
|
|
|
|
LastPrefetchPhaseIdx = PrefetchPhaseIdx;
|
|
|
|
//
|
|
// Prefetch the data and image pages for each boot prefetching phase,
|
|
// waiting for & signaling the events matching those phases so boot
|
|
// is synchronized with prefetching. (I.e. we prefetch pages for a boot
|
|
// phase before we start that boot phase.)
|
|
//
|
|
|
|
for (PrefetchPhaseIdx = 0; PrefetchPhaseIdx < LastPrefetchPhaseIdx; PrefetchPhaseIdx++) {
|
|
|
|
//
|
|
// If this is the video init prefetching phase, wait for video
|
|
// initialization to begin.
|
|
//
|
|
|
|
if (PrefetchPhaseIdx == VideoInitPrefetchingPhaseIdx) {
|
|
KeWaitForSingleObject(&BootPrefetcher->VideoInitStarted,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
for (CursorIdx = 0; CursorIdx < MaxCursor; CursorIdx++) {
|
|
|
|
Cursor = &Cursors[CursorIdx];
|
|
|
|
NumPagesToPrefetch = Cursor->NumPagesForPhase[PrefetchPhaseIdx];
|
|
|
|
//
|
|
// For prefetch phases before SMSS is launched keep an eye on
|
|
// how much memory is still available to prefetch into so we
|
|
// don't cannibalize ourselves. After SMSS our heuristics on
|
|
// standby-list composition do not make sense.
|
|
//
|
|
|
|
if (PrefetchPhaseIdx <= PreSmssPrefetchingPhaseIdx) {
|
|
|
|
//
|
|
// Check if we have available memory to prefetch more.
|
|
//
|
|
|
|
if (TotalPagesPrefetched + BootPrefetchAdjustment >= MmAvailablePages) {
|
|
|
|
OutOfAvailablePages = TRUE;
|
|
|
|
NumPagesToPrefetch = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check if we have to adjust NumPagesToPrefetch and prefetch
|
|
// one last chunk.
|
|
//
|
|
|
|
AvailablePages = MmAvailablePages;
|
|
AvailablePages -= (TotalPagesPrefetched + BootPrefetchAdjustment);
|
|
|
|
if (AvailablePages < NumPagesToPrefetch) {
|
|
NumPagesToPrefetch = AvailablePages;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NumPagesToPrefetch) {
|
|
|
|
Status = CcPfPrefetchSections(&PrefetchHeader,
|
|
Cursor->PrefetchType,
|
|
&Cursor->StartCursor,
|
|
NumPagesToPrefetch,
|
|
&NumPagesPrefetched,
|
|
&Cursor->EndCursor);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
NumPagesPrefetched = 0;
|
|
}
|
|
|
|
//
|
|
// Update our position.
|
|
//
|
|
|
|
Cursor->StartCursor = Cursor->EndCursor;
|
|
|
|
TotalPagesPrefetched += NumPagesPrefetched;
|
|
|
|
}
|
|
|
|
//
|
|
// Note that we are done with this prefetching phase and
|
|
// system boot can continue.
|
|
//
|
|
|
|
if (PrefetchPhaseIdx == SystemDriverPrefetchingPhaseIdx) {
|
|
KeSetEvent(&BootPrefetcher->SystemDriversPrefetchingDone,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
}
|
|
|
|
if (PrefetchPhaseIdx == PreSmssPrefetchingPhaseIdx) {
|
|
KeSetEvent(&BootPrefetcher->PreSmssPrefetchingDone,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
}
|
|
|
|
if (PrefetchPhaseIdx == VideoInitPrefetchingPhaseIdx) {
|
|
KeSetEvent(&BootPrefetcher->VideoInitPrefetchingDone,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Log that we are done with boot prefetch disk I/Os.
|
|
//
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_DISK_IO)) {
|
|
|
|
LogEntry.Action = 1;
|
|
LogEntry.Status = Status;
|
|
LogEntry.Pages = TotalPagesPrefetched;
|
|
|
|
PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PREFETCH_INFORMATION,
|
|
&LogEntry,
|
|
sizeof(LogEntry));
|
|
}
|
|
|
|
//
|
|
// Make sure all the events system may wait for before proceeding with
|
|
// boot are signaled.
|
|
//
|
|
|
|
KeSetEvent(&BootPrefetcher->SystemDriversPrefetchingDone,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
|
|
KeSetEvent(&BootPrefetcher->PreSmssPrefetchingDone,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
|
|
KeSetEvent(&BootPrefetcher->VideoInitPrefetchingDone,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
|
|
//
|
|
// Let MM know that we are done prefetching for boot.
|
|
//
|
|
|
|
CcPfPrefetchingForBoot = FALSE;
|
|
|
|
//
|
|
// Cleanup prefetching context.
|
|
//
|
|
|
|
CcPfCleanupPrefetchHeader(&PrefetchHeader);
|
|
|
|
if (PrefetchHeader.Scenario) {
|
|
ExFreePool(PrefetchHeader.Scenario);
|
|
}
|
|
|
|
DBGPR((CCPFID,PFTRC,"CCPF: BootWorker()=%x,%d\n",Status,(ULONG)OutOfAvailablePages));
|
|
}
|
|
|
|
NTSTATUS
|
|
CcPfBootQueueEndTraceTimer (
|
|
PLARGE_INTEGER Timeout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates and queues a timer that will attempt to end
|
|
the boot trace when it fires.
|
|
|
|
Arguments:
|
|
|
|
Timeout - Timeout for the timer.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
Environment:
|
|
|
|
Kernel mode. IRQL <= PASSIVE_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID Allocation;
|
|
PKTIMER Timer;
|
|
PKDPC Dpc;
|
|
ULONG AllocationSize;
|
|
NTSTATUS Status;
|
|
BOOLEAN TimerAlreadyQueued;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
Allocation = NULL;
|
|
|
|
//
|
|
// Make a single allocation for the timer and dpc.
|
|
//
|
|
|
|
AllocationSize = sizeof(KTIMER);
|
|
AllocationSize += sizeof(KDPC);
|
|
|
|
Allocation = ExAllocatePoolWithTag(NonPagedPool,
|
|
AllocationSize,
|
|
CCPF_ALLOC_BOOTWRKR_TAG);
|
|
|
|
if (!Allocation) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
Timer = Allocation;
|
|
Dpc = (PKDPC)(Timer + 1);
|
|
|
|
//
|
|
// Initialize the timer and DPC. We'll be passing the allocation to the
|
|
// queued DPC so it can be freed.
|
|
//
|
|
|
|
KeInitializeTimer(Timer);
|
|
KeInitializeDpc(Dpc, CcPfEndBootTimerRoutine, Allocation);
|
|
|
|
//
|
|
// Queue the timer.
|
|
//
|
|
|
|
TimerAlreadyQueued = KeSetTimer(Timer, *Timeout, Dpc);
|
|
|
|
CCPF_ASSERT(!TimerAlreadyQueued);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Allocation) {
|
|
ExFreePool(Allocation);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CcPfEndBootTimerRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked as the DPC handler for a timer queued to
|
|
mark the end of boot and end the boot trace if one is active.
|
|
|
|
Arguments:
|
|
|
|
DeferredContext - Allocated memory for the timer & dpc that need
|
|
to be freed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode. IRQL == DISPATCH_LEVEL.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PCCPF_TRACE_HEADER BootTrace;
|
|
PERFINFO_BOOT_PHASE_START LogEntry;
|
|
|
|
UNREFERENCED_PARAMETER (Dpc);
|
|
UNREFERENCED_PARAMETER (SystemArgument1);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
BootTrace = NULL;
|
|
|
|
//
|
|
// Is the boot trace still active?
|
|
//
|
|
|
|
BootTrace = CcPfReferenceProcessTrace(PsInitialSystemProcess);
|
|
|
|
if (BootTrace && BootTrace->ScenarioType == PfSystemBootScenarioType) {
|
|
|
|
//
|
|
// Is somebody already ending the boot trace?
|
|
//
|
|
|
|
if (!InterlockedCompareExchange(&BootTrace->EndTraceCalled, 1, 0)) {
|
|
|
|
//
|
|
// We set EndTraceCalled from 0 to 1. Queue the
|
|
// workitem to end the trace.
|
|
//
|
|
|
|
ExQueueWorkItem(&BootTrace->EndTraceWorkItem, DelayedWorkQueue);
|
|
|
|
//
|
|
// Log that we are ending the boot trace.
|
|
//
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_LOADER)) {
|
|
|
|
LogEntry.Phase = PfMaxBootPhaseId;
|
|
|
|
PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PHASE_START,
|
|
&LogEntry,
|
|
sizeof(LogEntry));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the memory allocated for the timer and dpc.
|
|
//
|
|
|
|
CCPF_ASSERT(DeferredContext);
|
|
ExFreePool(DeferredContext);
|
|
|
|
if (BootTrace) {
|
|
CcPfDecRef(&BootTrace->RefCount);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|