windows-nt/Source/XPSP1/NT/base/ntos/cache/prefparm.c
2020-09-26 16:20:57 +08:00

1701 lines
43 KiB
C

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
prefparm.c
Abstract:
This module contains the code for prefetcher parameter handling.
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(INIT, CcPfParametersInitialize)
#pragma alloc_text(INIT, CcPfParametersSetDefaults)
#pragma alloc_text(PAGE, CcPfParametersRead)
#pragma alloc_text(PAGE, CcPfParametersSave)
#pragma alloc_text(PAGE, CcPfParametersVerify)
#pragma alloc_text(PAGE, CcPfParametersWatcher)
#pragma alloc_text(PAGE, CcPfParametersSetChangedEvent)
#pragma alloc_text(PAGE, CcPfGetParameter)
#pragma alloc_text(PAGE, CcPfSetParameter)
#pragma alloc_text(PAGE, CcPfDetermineEnablePrefetcher)
#pragma alloc_text(PAGE, CcPfIsHostingApplication)
#endif // ALLOC_PRAGMA
//
// Globals:
//
extern CCPF_PREFETCHER_GLOBALS CcPfGlobals;
//
// Constants:
//
//
// The following are used as prefixs for the value names for registry
// parameters that are per scenario type.
//
WCHAR *CcPfAppLaunchScenarioTypePrefix = L"AppLaunch";
WCHAR *CcPfBootScenarioTypePrefix = L"Boot";
//
// Routines for prefetcher parameter handling.
//
NTSTATUS
CcPfParametersInitialize (
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters
)
/*++
Routine Description:
Initializes specified prefetcher parameters structure.
Arguments:
PrefetcherParameters - Pointer to structure to initialize.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
Notes:
The code & local constants for this function gets discarded after system boots.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING KeyName;
NTSTATUS Status;
//
// Zero out the structure. This initializes:
// ParametersVersion
//
RtlZeroMemory(PrefetcherParameters, sizeof(*PrefetcherParameters));
//
// Initialize the lock protecting the parameters and parameters
// version. Each time parameters are updated, the version is
// bumped.
//
ExInitializeResourceLite(&PrefetcherParameters->ParametersLock);
//
// Initialize the workitem used for registry notifications on the
// parameters key.
//
ExInitializeWorkItem(&PrefetcherParameters->RegistryWatchWorkItem,
CcPfParametersWatcher,
PrefetcherParameters);
//
// Set default parameters.
//
CcPfParametersSetDefaults(PrefetcherParameters);
//
// Create / Open the registry key that contains our parameters.
//
RtlInitUnicodeString(&KeyName, CCPF_PARAMETERS_KEY);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwCreateKey(&PrefetcherParameters->ParametersKey,
KEY_ALL_ACCESS,
&ObjectAttributes,
0,
NULL,
REG_OPTION_NON_VOLATILE,
0);
if (NT_SUCCESS(Status)) {
//
// Update the default parameters with those in the registry.
//
Status = CcPfParametersRead(PrefetcherParameters);
if (!NT_SUCCESS(Status)) {
DBGPR((CCPFID,PFERR,"CCPF: Init-FailedReadParams=%x\n",Status));
}
//
// Request notification when something changes in the
// prefetcher parameters key.
//
Status = ZwNotifyChangeKey(PrefetcherParameters->ParametersKey,
NULL,
(PIO_APC_ROUTINE)&PrefetcherParameters->RegistryWatchWorkItem,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
&PrefetcherParameters->RegistryWatchIosb,
REG_LEGAL_CHANGE_FILTER,
FALSE,
&PrefetcherParameters->RegistryWatchBuffer,
sizeof(PrefetcherParameters->RegistryWatchBuffer),
TRUE);
if (!NT_SUCCESS(Status)) {
//
// Although we could not register a notification, this
// is not a fatal error.
//
DBGPR((CCPFID,PFERR,"CCPF: Init-FailedSetParamNotify=%x\n",Status));
}
} else {
DBGPR((CCPFID,PFERR,"CCPF: Init-FailedCreateParamKey=%x\n",Status));
}
return Status;
}
VOID
CcPfParametersSetDefaults (
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters
)
/*++
Routine Description:
Initializes specified parameters structure to default values.
Arguments:
Parameters - Pointer to structure to initialize.
Return Value:
None.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
Notes:
The code & local constants for this function gets discarded after system boots.
--*/
{
PPF_SYSTEM_PREFETCH_PARAMETERS Parameters;
PPF_TRACE_LIMITS TraceLimits;
PF_SCENARIO_TYPE ScenarioType;
//
// Initialize locals.
//
Parameters = &PrefetcherParameters->Parameters;
for (ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
//
// PfSvNotSpecified is currently treated as disabled.
//
Parameters->EnableStatus[ScenarioType] = PfSvNotSpecified;
//
// Trace limits are determined based on scenario type.
//
TraceLimits = &Parameters->TraceLimits[ScenarioType];
switch(ScenarioType) {
case PfApplicationLaunchScenarioType:
TraceLimits->MaxNumPages = 4000;
TraceLimits->MaxNumSections = 170;
TraceLimits->TimerPeriod = (-1 * 1000 * 1000 * 10);
PrefetcherParameters->ScenarioTypePrefixes[ScenarioType] =
CcPfAppLaunchScenarioTypePrefix;
break;
case PfSystemBootScenarioType:
TraceLimits->MaxNumPages = 128000;
TraceLimits->MaxNumSections = 4080;
TraceLimits->TimerPeriod = (-1 * 12000 * 1000 * 10);
PrefetcherParameters->ScenarioTypePrefixes[ScenarioType] =
CcPfBootScenarioTypePrefix;
break;
default:
//
// We should be handling all scenario types above.
//
CCPF_ASSERT(FALSE);
TraceLimits->MaxNumPages = PF_MAXIMUM_PAGES;
TraceLimits->MaxNumSections = PF_MAXIMUM_SECTIONS;
TraceLimits->TimerPeriod = (-1 * 1000 * 1000 * 10);
PrefetcherParameters->ScenarioTypePrefixes[ScenarioType] = L"XXX";
}
}
//
// These limits ensure that we don't monopolize system resources
// for prefetching.
//
Parameters->MaxNumActiveTraces = 8;
Parameters->MaxNumSavedTraces = 8;
//
// This is the default directory under SystemRoot where we
// find prefetch instructions for scenarios. During upgrades
// we remove the contents of this directory, so "Prefetch" is
// hardcoded in txtsetup.inx.
//
wcsncpy(Parameters->RootDirPath,
L"Prefetch",
PF_MAX_PREFETCH_ROOT_PATH);
Parameters->RootDirPath[PF_MAX_PREFETCH_ROOT_PATH - 1] = 0;
//
// This is the default list of known hosting applications.
//
wcsncpy(Parameters->HostingApplicationList,
L"DLLHOST.EXE,MMC.EXE,RUNDLL32.EXE",
PF_HOSTING_APP_LIST_MAX_CHARS);
Parameters->HostingApplicationList[PF_HOSTING_APP_LIST_MAX_CHARS - 1] = 0;
//
// Make sure the default parameters make sense.
//
CCPF_ASSERT(NT_SUCCESS(CcPfParametersVerify(Parameters)));
}
NTSTATUS
CcPfParametersRead (
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters
)
/*++
Routine Description:
This routine updates the parameters structure with the
parameters in the registry.
Keep the value names that are used in sync with the function to
save the parameters.
Arguments:
PrefetcherParameters - Pointer to parameters.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
NTSTATUS Status;
PF_SYSTEM_PREFETCH_PARAMETERS Parameters;
PPF_TRACE_LIMITS TraceLimits;
PF_SCENARIO_TYPE ScenarioType;
WCHAR ValueName[CCPF_MAX_PARAMETER_NAME_LENGTH];
WCHAR *ValueNamePrefix;
HANDLE ParametersKey;
BOOLEAN EnableStatusSpecified;
ULONG EnablePrefetcher;
BOOLEAN AcquiredParametersLock;
ULONG Length;
LONG CurrentVersion;
ULONG RetryCount;
PKTHREAD CurrentThread;
//
// Initialize locals.
//
CurrentThread = KeGetCurrentThread ();
AcquiredParametersLock = FALSE;
RetryCount = 0;
DBGPR((CCPFID,PFTRC,"CCPF: ParametersRead()\n"));
//
// If we could not initialize the parameters key, we would fail
// all the following ops miserably.
//
if (!PrefetcherParameters->ParametersKey) {
Status = STATUS_REINITIALIZATION_NEEDED;
goto cleanup;
}
do {
//
// Get the parameters lock shared.
//
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceSharedLite(&PrefetcherParameters->ParametersLock, TRUE);
AcquiredParametersLock = TRUE;
ParametersKey = PrefetcherParameters->ParametersKey;
//
// Save current version of parameters. Each time parameters gets
// updated, the version is bumped.
//
CurrentVersion = PrefetcherParameters->ParametersVersion;
//
// Copy over existing parameters to the parameters structure we
// are building. This way, if we cannot get a value from the
// registry we'll keep the value we already have.
//
Parameters = PrefetcherParameters->Parameters;
//
// Read the prefetcher enable value. Depending on whether it is
// specified and if so its value we will set enable status for
// prefetch scenario types.
//
Length = sizeof(EnablePrefetcher);
Status = CcPfGetParameter(ParametersKey,
L"EnablePrefetcher",
REG_DWORD,
&EnablePrefetcher,
&Length);
if (!NT_SUCCESS(Status)) {
//
// Enable status is not specified or we cannot access it.
//
EnableStatusSpecified = FALSE;
} else {
EnableStatusSpecified = TRUE;
}
//
// Get per scenario parameters.
//
for (ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
ValueNamePrefix = PrefetcherParameters->ScenarioTypePrefixes[ScenarioType];
//
// Determine enable status. If EnableStatusSpecified, whether
// prefeching for this scenario type is on or off is
// determined by the ScenarioType'th bit in EnablePrefetcher.
//
if (EnableStatusSpecified) {
if (EnablePrefetcher & (1 << ScenarioType)) {
Parameters.EnableStatus[ScenarioType] = PfSvEnabled;
} else {
Parameters.EnableStatus[ScenarioType] = PfSvDisabled;
}
} else {
Parameters.EnableStatus[ScenarioType] = PfSvNotSpecified;
}
//
// Update trace limits for this scenario type. Ignore return
// value from GetParameter since the value may not be
// specified in the registry. If so the current value is kept
// intact.
//
TraceLimits = &Parameters.TraceLimits[ScenarioType];
wcscpy(ValueName, ValueNamePrefix);
wcscat(ValueName, L"MaxNumPages");
Length = sizeof(TraceLimits->MaxNumPages);
CcPfGetParameter(ParametersKey,
ValueName,
REG_DWORD,
&TraceLimits->MaxNumPages,
&Length);
wcscpy(ValueName, ValueNamePrefix);
wcscat(ValueName, L"MaxNumSections");
Length = sizeof(TraceLimits->MaxNumSections);
CcPfGetParameter(ParametersKey,
ValueName,
REG_DWORD,
&TraceLimits->MaxNumSections,
&Length);
wcscpy(ValueName, ValueNamePrefix);
wcscat(ValueName, L"TimerPeriod");
Length = sizeof(TraceLimits->TimerPeriod);
CcPfGetParameter(ParametersKey,
ValueName,
REG_BINARY,
&TraceLimits->TimerPeriod,
&Length);
}
//
// Update maximum number of active traces.
//
Length = sizeof(Parameters.MaxNumActiveTraces);
CcPfGetParameter(ParametersKey,
L"MaxNumActiveTraces",
REG_DWORD,
&Parameters.MaxNumActiveTraces,
&Length);
//
// Update maximum number of saved traces.
//
Length = sizeof(Parameters.MaxNumSavedTraces);
CcPfGetParameter(ParametersKey,
L"MaxNumSavedTraces",
REG_DWORD,
&Parameters.MaxNumSavedTraces,
&Length);
//
// Update the root directory path.
//
Length = sizeof(Parameters.RootDirPath);
CcPfGetParameter(ParametersKey,
L"RootDirPath",
REG_SZ,
Parameters.RootDirPath,
&Length);
//
// Update list of known hosting applications.
//
Length = sizeof(Parameters.HostingApplicationList);
CcPfGetParameter(ParametersKey,
L"HostingAppList",
REG_SZ,
Parameters.HostingApplicationList,
&Length);
Parameters.HostingApplicationList[PF_HOSTING_APP_LIST_MAX_CHARS - 1] = 0;
_wcsupr(Parameters.HostingApplicationList);
//
// Verify the parameters updated from the registry.
//
Status = CcPfParametersVerify(&Parameters);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
//
// Release the shared lock and acquire it exclusive.
//
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceExclusiveLite(&PrefetcherParameters->ParametersLock, TRUE);
//
// Check if somebody already updated the parameters before us.
//
if (CurrentVersion != PrefetcherParameters->ParametersVersion) {
//
// Bummer. Somebody updated parameters when we released
// our shared lock to acquire it exclusive. We have to try
// again. The default values we used for parameters that
// were not in the registry may have been changed.
//
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
AcquiredParametersLock = FALSE;
RetryCount++;
continue;
}
//
// We are updating the parameters, bump the version.
//
PrefetcherParameters->ParametersVersion++;
PrefetcherParameters->Parameters = Parameters;
//
// Release the exclusive lock and break out.
//
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
AcquiredParametersLock = FALSE;
break;
} while (RetryCount < 10);
//
// See if we looped too many times and could not achive updating
// the parameters.
//
if (RetryCount >= 10) {
Status = STATUS_RETRY;
goto cleanup;
}
//
// Otherwise we were successful.
//
Status = STATUS_SUCCESS;
cleanup:
if (AcquiredParametersLock) {
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
}
DBGPR((CCPFID,PFTRC,"CCPF: ParametersRead()=%x\n", Status));
return Status;
}
NTSTATUS
CcPfParametersSave (
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters
)
/*++
Routine Description:
This routine updates the registry with the specified prefetch
parameters.
Arguments:
PrefetcherParameters - Pointer to parameters structure.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
NTSTATUS Status;
PPF_TRACE_LIMITS TraceLimits;
PF_SCENARIO_TYPE ScenarioType;
WCHAR ValueName[CCPF_MAX_PARAMETER_NAME_LENGTH];
WCHAR *ValueNamePrefix;
HANDLE ParametersKey;
BOOLEAN EnableStatusSpecified;
ULONG EnablePrefetcher;
BOOLEAN AcquiredParametersLock;
ULONG Length;
PPF_SYSTEM_PREFETCH_PARAMETERS Parameters;
PKTHREAD CurrentThread;
//
// Initialize locals.
//
CurrentThread = KeGetCurrentThread ();
ParametersKey = PrefetcherParameters->ParametersKey;
Parameters = &PrefetcherParameters->Parameters;
AcquiredParametersLock = FALSE;
DBGPR((CCPFID,PFTRC,"CCPF: ParametersSave()\n"));
//
// If we could not initialize the parameters key, we would fail
// all the following ops miserably.
//
if (!PrefetcherParameters->ParametersKey) {
Status = STATUS_REINITIALIZATION_NEEDED;
goto cleanup;
}
//
// Get the parameters lock shared.
//
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceSharedLite(&PrefetcherParameters->ParametersLock, TRUE);
AcquiredParametersLock = TRUE;
//
// Build up the prefetcher enable value.
//
EnableStatusSpecified = FALSE;
EnablePrefetcher = 0;
for (ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
//
// By default prefetching for all scenario types will be
// disabled, except it is explicitly enabled.
//
if (Parameters->EnableStatus[ScenarioType] == PfSvEnabled) {
EnablePrefetcher |= (1 << ScenarioType);
}
//
// Even if enable status for one scenario type is specified,
// we have to save the enable prefetcher key.
//
if (Parameters->EnableStatus[ScenarioType] != PfSvNotSpecified) {
EnableStatusSpecified = TRUE;
}
}
if (EnableStatusSpecified) {
//
// Save the prefetcher enable key.
//
Length = sizeof(EnablePrefetcher);
Status = CcPfSetParameter(ParametersKey,
L"EnablePrefetcher",
REG_DWORD,
&EnablePrefetcher,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
}
//
// Save per scenario parameters.
//
for (ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
ValueNamePrefix = PrefetcherParameters->ScenarioTypePrefixes[ScenarioType];
//
// Update trace limits for this scenario type.
//
TraceLimits = &Parameters->TraceLimits[ScenarioType];
wcscpy(ValueName, ValueNamePrefix);
wcscat(ValueName, L"MaxNumPages");
Length = sizeof(TraceLimits->MaxNumPages);
Status = CcPfSetParameter(ParametersKey,
ValueName,
REG_DWORD,
&TraceLimits->MaxNumPages,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
wcscpy(ValueName, ValueNamePrefix);
wcscat(ValueName, L"MaxNumSections");
Length = sizeof(TraceLimits->MaxNumSections);
Status = CcPfSetParameter(ParametersKey,
ValueName,
REG_DWORD,
&TraceLimits->MaxNumSections,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
wcscpy(ValueName, ValueNamePrefix);
wcscat(ValueName, L"TimerPeriod");
Length = sizeof(TraceLimits->TimerPeriod);
Status = CcPfSetParameter(ParametersKey,
ValueName,
REG_BINARY,
&TraceLimits->TimerPeriod,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
}
//
// Update maximum number of active traces.
//
Length = sizeof(Parameters->MaxNumActiveTraces);
Status = CcPfSetParameter(ParametersKey,
L"MaxNumActiveTraces",
REG_DWORD,
&Parameters->MaxNumActiveTraces,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
//
// Update maximum number of saved traces.
//
Length = sizeof(Parameters->MaxNumSavedTraces);
Status = CcPfSetParameter(ParametersKey,
L"MaxNumSavedTraces",
REG_DWORD,
&Parameters->MaxNumSavedTraces,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
//
// Update the root directory path.
//
Length = (wcslen(Parameters->RootDirPath) + 1) * sizeof(WCHAR);
Status = CcPfSetParameter(ParametersKey,
L"RootDirPath",
REG_SZ,
Parameters->RootDirPath,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
//
// Update the hosting application list path.
//
Length = (wcslen(Parameters->HostingApplicationList) + 1) * sizeof(WCHAR);
Status = CcPfSetParameter(ParametersKey,
L"HostingAppList",
REG_SZ,
Parameters->HostingApplicationList,
Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
Status = STATUS_SUCCESS;
cleanup:
if (AcquiredParametersLock) {
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
}
DBGPR((CCPFID,PFTRC,"CCPF: ParametersSave()=%x\n", Status));
return Status;
}
NTSTATUS
CcPfParametersVerify (
PPF_SYSTEM_PREFETCH_PARAMETERS Parameters
)
/*++
Routine Description:
This routine verifies that the specified parameters structure is
valid and within sanity limits.
Arguments:
Parameters - Pointer to parameters structure.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
NTSTATUS Status;
ULONG FailedCheckId;
ULONG CharIdx;
BOOLEAN FoundNUL;
PF_SCENARIO_TYPE ScenarioType;
PPF_TRACE_LIMITS TraceLimits;
//
// Initialize locals.
//
Status = STATUS_INVALID_PARAMETER;
FailedCheckId = 0;
DBGPR((CCPFID,PFTRC,"CCPF: ParametersVerify\n"));
//
// Make sure RootDirPath is NUL terminated.
//
FoundNUL = FALSE;
for (CharIdx = 0; CharIdx < PF_MAX_PREFETCH_ROOT_PATH; CharIdx++) {
if (Parameters->RootDirPath[CharIdx] == 0) {
FoundNUL = TRUE;
break;
}
}
if (FoundNUL == FALSE) {
FailedCheckId = 10;
goto cleanup;
}
//
// Make sure HostingApplicationList is NUL terminated.
//
FoundNUL = FALSE;
for (CharIdx = 0; CharIdx < PF_HOSTING_APP_LIST_MAX_CHARS; CharIdx++) {
if (Parameters->HostingApplicationList[CharIdx] == 0) {
FoundNUL = TRUE;
break;
}
}
if (FoundNUL == FALSE) {
FailedCheckId = 15;
goto cleanup;
}
//
// Make sure all per scenario type parameters types are within
// sanity limits.
//
for (ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
if (Parameters->EnableStatus[ScenarioType] >= PfSvMaxEnableStatus) {
FailedCheckId = 20;
goto cleanup;
}
//
// Check trace limits.
//
TraceLimits = &Parameters->TraceLimits[ScenarioType];
if (TraceLimits->MaxNumPages > PF_MAXIMUM_PAGES) {
FailedCheckId = 30;
goto cleanup;
}
if (TraceLimits->MaxNumSections > PF_MAXIMUM_SECTIONS) {
FailedCheckId = 40;
goto cleanup;
}
if ((TraceLimits->TimerPeriod < PF_MAXIMUM_TIMER_PERIOD) ||
(TraceLimits->TimerPeriod >= 0)) {
FailedCheckId = 50;
goto cleanup;
}
}
//
// Check limits on active/saved traces.
//
if (Parameters->MaxNumActiveTraces > PF_MAXIMUM_ACTIVE_TRACES) {
FailedCheckId = 60;
goto cleanup;
}
if (Parameters->MaxNumSavedTraces > PF_MAXIMUM_SAVED_TRACES) {
FailedCheckId = 70;
goto cleanup;
}
//
// We passed all the checks.
//
Status = STATUS_SUCCESS;
cleanup:
DBGPR((CCPFID,PFTRC,"CCPF: ParametersVerify()=%x,%d\n", Status, FailedCheckId));
return Status;
}
VOID
CcPfParametersWatcher(
IN PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters
)
/*++
Routine Description:
This routine gets called when our parameters in the registry change.
Arguments:
PrefetcherParameters - Pointer to parameters structure.
Return Value:
None.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
NTSTATUS Status;
UNICODE_STRING KeyName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE ParametersKey;
PKTHREAD CurrentThread;
HANDLE TempHandle;
DBGPR((CCPFID,PFTRC,"CCPF: ParametersWatcher()\n"));
//
// In order to have setup a registry watch, we should have
// initialized the parameters key successfully.
//
CCPF_ASSERT(PrefetcherParameters->ParametersKey);
//
// Our change notify triggered. Request further notification. But
// first wait until we can get the parameters lock exclusive, so
// while we are saving parameters to the registry we don't kick
// off a notification for each key.
//
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceExclusiveLite(&PrefetcherParameters->ParametersLock, TRUE);
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
Status = ZwNotifyChangeKey(PrefetcherParameters->ParametersKey,
NULL,
(PIO_APC_ROUTINE)&PrefetcherParameters->RegistryWatchWorkItem,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
&PrefetcherParameters->RegistryWatchIosb,
REG_LEGAL_CHANGE_FILTER,
FALSE,
&PrefetcherParameters->RegistryWatchBuffer,
sizeof(PrefetcherParameters->RegistryWatchBuffer),
TRUE);
if (!NT_SUCCESS(Status)) {
//
// Somebody may have deleted the key. We have to recreate it then.
//
if (Status == STATUS_KEY_DELETED) {
RtlInitUnicodeString(&KeyName, CCPF_PARAMETERS_KEY);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwCreateKey(&ParametersKey,
KEY_ALL_ACCESS,
&ObjectAttributes,
0,
NULL,
REG_OPTION_NON_VOLATILE,
0);
if (!NT_SUCCESS(Status)) {
DBGPR((CCPFID,PFERR,"CCPF: ParametersWatcher-FailedRecreate=%x\n",Status));
return;
}
//
// Update global key handle.
//
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceExclusiveLite(&PrefetcherParameters->ParametersLock, TRUE);
TempHandle = PrefetcherParameters->ParametersKey;
PrefetcherParameters->ParametersKey = ParametersKey;
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
ZwClose(TempHandle);
//
// Retry setting a notification again.
//
Status = ZwNotifyChangeKey(PrefetcherParameters->ParametersKey,
NULL,
(PIO_APC_ROUTINE)&PrefetcherParameters->RegistryWatchWorkItem,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
&PrefetcherParameters->RegistryWatchIosb,
REG_LEGAL_CHANGE_FILTER,
FALSE,
&PrefetcherParameters->RegistryWatchBuffer,
sizeof(PrefetcherParameters->RegistryWatchBuffer),
TRUE);
if (!NT_SUCCESS(Status)) {
DBGPR((CCPFID,PFERR,"CCPF: ParametersWatcher-FailedReSetNotify=%x\n",Status));
return;
}
} else {
DBGPR((CCPFID,PFERR,"CCPF: ParametersWatcher-FailedSetNotify=%x\n",Status));
return;
}
}
//
// Update the global parameters.
//
Status = CcPfParametersRead(PrefetcherParameters);
if (NT_SUCCESS(Status)) {
//
// Determine if prefetching is enabled.
//
CcPfDetermineEnablePrefetcher();
//
// Set the event so the service queries for the latest parameters.
//
CcPfParametersSetChangedEvent(PrefetcherParameters);
}
return;
}
NTSTATUS
CcPfParametersSetChangedEvent(
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters
)
/*++
Routine Description:
This routine tries to open and set the event that tells the
service system prefetch parameters have changed.
Arguments:
None.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
NTSTATUS Status;
UNICODE_STRING EventName;
OBJECT_ATTRIBUTES EventObjAttr;
HANDLE EventHandle;
PKTHREAD CurrentThread;
DBGPR((CCPFID,PFTRC,"CCPF: ParametersSetChangedEvent()\n"));
//
// If we have already opened the event, just signal it.
//
if (PrefetcherParameters->ParametersChangedEvent) {
ZwSetEvent(PrefetcherParameters->ParametersChangedEvent, NULL);
Status = STATUS_SUCCESS;
} else {
//
// Try to open the event. We don't open this at initialization
// because our service may not have started to create this
// event yet. If csrss.exe has not initialized, we may not
// even have the BaseNamedObjects object directory created, in
// which Win32 events reside.
//
RtlInitUnicodeString(&EventName, PF_PARAMETERS_CHANGED_EVENT_NAME);
InitializeObjectAttributes(&EventObjAttr,
&EventName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenEvent(&EventHandle,
EVENT_ALL_ACCESS,
&EventObjAttr);
if (NT_SUCCESS(Status)) {
//
// Acquire the lock and set the global handle.
//
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceExclusiveLite(&PrefetcherParameters->ParametersLock, TRUE);
if (!PrefetcherParameters->ParametersChangedEvent) {
//
// Set the global handle.
//
PrefetcherParameters->ParametersChangedEvent = EventHandle;
CCPF_ASSERT(EventHandle);
EventHandle = NULL;
}
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
if (EventHandle != NULL) {
//
// Somebody already initialized the global handle
// before us. Close our handle and use the one they
// initialized.
//
ZwClose(EventHandle);
}
//
// We have an event now. Signal it.
//
ZwSetEvent(PrefetcherParameters->ParametersChangedEvent, NULL);
}
}
DBGPR((CCPFID,PFTRC,"CCPF: ParametersSetChangedEvent()=%x\n", Status));
return Status;
}
NTSTATUS
CcPfGetParameter (
HANDLE ParametersKey,
WCHAR *ValueNameBuffer,
ULONG ValueType,
PVOID Value,
ULONG *ValueSize
)
/*++
Routine Description:
This routine queries a value under the specified registry into the
specified buffer. Contents of Value and ValueSize are not changed
if returning failure.
Arguments:
ParametersKey - Handle to key to query value under.
ValueNameBuffer - Name of the value.
ValueType - What the type of that value should be. (e.g. REG_DWORD).
Value - Queried value data gets put here.
ValueSize - Size of Value buffer in bytes. On successful return
this is set to number of bytes copied into Value.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
UNICODE_STRING ValueName;
CHAR Buffer[CCPF_MAX_PARAMETER_VALUE_BUFFER];
PKEY_VALUE_PARTIAL_INFORMATION ValueBuffer;
ULONG Length;
NTSTATUS Status;
//
// Initialize locals.
//
ValueBuffer = (PKEY_VALUE_PARTIAL_INFORMATION) Buffer;
Length = CCPF_MAX_PARAMETER_VALUE_BUFFER;
RtlInitUnicodeString(&ValueName, ValueNameBuffer);
DBGPR((CCPFID,PFTRC,"CCPF: GetParameter(%ws,%x)\n", ValueNameBuffer, ValueType));
//
// Query value.
//
Status = ZwQueryValueKey(ParametersKey,
&ValueName,
KeyValuePartialInformation,
ValueBuffer,
Length,
&Length);
if (!NT_SUCCESS(Status)) {
goto cleanup;
}
//
// Make sure ZwQueryValue returns valid information.
//
if (Length < sizeof(KEY_VALUE_PARTIAL_INFORMATION)) {
CCPF_ASSERT(Length >= sizeof(KEY_VALUE_PARTIAL_INFORMATION));
Status = STATUS_UNSUCCESSFUL;
goto cleanup;
}
//
// Check value type.
//
if (ValueBuffer->Type != ValueType) {
Status = STATUS_OBJECT_TYPE_MISMATCH;
goto cleanup;
}
//
// Check if data will fit into the buffer caller passed in.
//
if (ValueBuffer->DataLength > *ValueSize) {
Status = STATUS_BUFFER_TOO_SMALL;
goto cleanup;
}
//
// Copy data into user's buffer.
//
RtlCopyMemory(Value, ValueBuffer->Data, ValueBuffer->DataLength);
//
// Set copied number of bytes.
//
*ValueSize = ValueBuffer->DataLength;
Status = STATUS_SUCCESS;
cleanup:
DBGPR((CCPFID,PFTRC,"CCPF: GetParameter(%ws)=%x\n", ValueNameBuffer, Status));
return Status;
}
NTSTATUS
CcPfSetParameter (
HANDLE ParametersKey,
WCHAR *ValueNameBuffer,
ULONG ValueType,
PVOID Value,
ULONG ValueSize
)
/*++
Routine Description:
This routine sets a parameter under the specified registry.
Arguments:
ParametersKey - Handle to key to query value under.
ValueNameBuffer - Name of the value.
ValueType - What the type of that value should be. (e.g. REG_DWORD).
Value - Data to save.
ValueSize - Size of Value buffer in bytes.
Return Value:
Status.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
UNICODE_STRING ValueName;
NTSTATUS Status;
//
// Initialize locals.
//
RtlInitUnicodeString(&ValueName, ValueNameBuffer);
DBGPR((CCPFID,PFTRC,"CCPF: SetParameter(%ws,%x)\n", ValueNameBuffer, ValueType));
//
// Save the value.
//
Status = ZwSetValueKey(ParametersKey,
&ValueName,
0,
ValueType,
Value,
ValueSize);
//
// Return the status.
//
DBGPR((CCPFID,PFTRC,"CCPF: SetParameter(%ws)=%x\n", ValueNameBuffer, Status));
return Status;
}
LOGICAL
CcPfDetermineEnablePrefetcher(
VOID
)
/*++
Routine Description:
This routine sets the global CcPfEnablePrefetcher based on the
EnableStatus'es for all scenario types in global parameters as
well as other factors, such as whether we have booted safe mode.
Note: Acquires Parameters lock exclusive.
Arguments:
None.
Return Value:
New value of CcPfEnablePrefetcher.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
PF_SCENARIO_TYPE ScenarioType;
LOGICAL EnablePrefetcher;
PKTHREAD CurrentThread;
BOOLEAN IgnoreBootScenarioType;
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters;
extern PF_BOOT_PHASE_ID CcPfBootPhase;
//
// Initialize locals.
//
EnablePrefetcher = FALSE;
PrefetcherParameters = &CcPfGlobals.Parameters;
CurrentThread = KeGetCurrentThread ();
//
// Ignore whether prefetching is enabled for boot, if we've
// already past the point in boot where this matters.
//
IgnoreBootScenarioType = (CcPfBootPhase >= PfSessionManagerInitPhase) ? TRUE : FALSE;
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceExclusiveLite(&PrefetcherParameters->ParametersLock, TRUE);
//
// If we have booted to safe mode, the prefetcher will be disabled.
//
if (InitSafeBootMode) {
EnablePrefetcher = FALSE;
} else {
//
// By default prefetching is disabled. If prefetching is
// enabled for any scenario type, then the prefetcher is
// enabled.
//
for (ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
//
// Skip enable status for the boot scenario if requested.
//
if (IgnoreBootScenarioType) {
if (ScenarioType == PfSystemBootScenarioType) {
continue;
}
}
if (PrefetcherParameters->Parameters.EnableStatus[ScenarioType] == PfSvEnabled) {
EnablePrefetcher = TRUE;
break;
}
}
}
//
// Update global enable status.
//
CcPfEnablePrefetcher = EnablePrefetcher;
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
return CcPfEnablePrefetcher;
}
BOOLEAN
CcPfIsHostingApplication(
IN PWCHAR ExecutableName
)
/*++
Routine Description:
This routine determines whether the specified executable is in the
list of known hosting applications, e.g. rundll32, dllhost etc.
Arguments:
ExecutableName - NUL terminated UPCASED executable name, e.g. "MMC.EXE"
Return Value:
TRUE - Executable is for a known hosting application.
FALSE - It is not.
Environment:
Kernel mode. IRQL == PASSIVE_LEVEL.
--*/
{
PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters;
PKTHREAD CurrentThread;
PWCHAR CurrentPosition;
PWCHAR ListStart;
PWCHAR ListEnd;
ULONG ExecutableNameLength;
BOOLEAN FoundInList;
//
// Initialize locals.
//
PrefetcherParameters = &CcPfGlobals.Parameters;
CurrentThread = KeGetCurrentThread();
ExecutableNameLength = wcslen(ExecutableName);
FoundInList = FALSE;
//
// Get the parameters lock for read.
//
KeEnterCriticalRegionThread(CurrentThread);
ExAcquireResourceSharedLite(&PrefetcherParameters->ParametersLock, TRUE);
//
// Search for executable in hosting application list.
//
ListStart = PrefetcherParameters->Parameters.HostingApplicationList;
ListEnd = ListStart + wcslen(PrefetcherParameters->Parameters.HostingApplicationList);
for (CurrentPosition = wcsstr(ListStart, ExecutableName);
CurrentPosition != NULL;
CurrentPosition = wcsstr(CurrentPosition + 1, ExecutableName)) {
//
// We should not go beyond the limits.
//
if (CurrentPosition < ListStart || CurrentPosition >= ListEnd) {
CCPF_ASSERT(CurrentPosition >= ListStart);
CCPF_ASSERT(CurrentPosition < ListEnd);
break;
}
//
// It should be the first item in the list or be preceded by a comma.
//
if (CurrentPosition != ListStart && *(CurrentPosition - 1) != L',') {
continue;
}
//
// It should be the last item in the list or be followed by a comma.
//
if (CurrentPosition + ExecutableNameLength != ListEnd &&
CurrentPosition[ExecutableNameLength] != L',') {
continue;
}
//
// We found it in the list.
//
FoundInList = TRUE;
break;
}
//
// Release the parameters lock.
//
ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
KeLeaveCriticalRegionThread(CurrentThread);
//
// Return whether the executable was found in the list.
//
return FoundInList;
}