You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
902 lines
22 KiB
902 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmgquota.c
|
|
|
|
Abstract:
|
|
|
|
The module contains CM routines to support Global Quota
|
|
|
|
Global Quota has little to do with NT's standard per-process/user
|
|
quota system. Global Quota is waying of controlling the aggregate
|
|
resource usage of the entire registry. It is used to manage space
|
|
consumption by objects which user apps create, but which are persistent
|
|
and therefore cannot be assigned to the quota of a user app.
|
|
|
|
Global Quota prevents the registry from consuming all of paged
|
|
pool, and indirectly controls how much disk it can consume.
|
|
Like the release 1 file systems, a single app can fill all the
|
|
space in the registry, but at least it cannot kill the system.
|
|
|
|
Memory objects used for known short times and protected by
|
|
serialization, or billable as quota objects, are not counted
|
|
in the global quota.
|
|
|
|
Author:
|
|
|
|
Bryan M. Willman (bryanwi) 13-Jan-1993
|
|
|
|
Revision History:
|
|
|
|
Dragos C Sambotin (dragoss) 04-Nov-1999
|
|
Charge quota only for bins in paged pool (volatile storage and bins crossing
|
|
the CM_VIEW_SIZE boundary).
|
|
|
|
--*/
|
|
|
|
#include "cmp.h"
|
|
|
|
VOID
|
|
CmpSystemHiveHysteresisWorker(
|
|
IN PVOID WorkItem
|
|
);
|
|
|
|
VOID
|
|
CmpRaiseSelfHealWarningWorker(
|
|
IN PVOID Arg
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmpClaimGlobalQuota)
|
|
#pragma alloc_text(PAGE,CmpReleaseGlobalQuota)
|
|
#pragma alloc_text(PAGE,CmpSetGlobalQuotaAllowed)
|
|
#pragma alloc_text(PAGE,CmpQuotaWarningWorker)
|
|
#pragma alloc_text(PAGE,CmQueryRegistryQuotaInformation)
|
|
#pragma alloc_text(PAGE,CmSetRegistryQuotaInformation)
|
|
#pragma alloc_text(PAGE,CmpCanGrowSystemHive)
|
|
#pragma alloc_text(PAGE,CmpSystemQuotaWarningWorker)
|
|
#pragma alloc_text(INIT,CmpComputeGlobalQuotaAllowed)
|
|
#pragma alloc_text(PAGE,CmpSystemHiveHysteresisWorker)
|
|
#pragma alloc_text(PAGE,CmpUpdateSystemHiveHysteresis)
|
|
#pragma alloc_text(PAGE,CmRegisterSystemHiveLimitCallback)
|
|
#pragma alloc_text(PAGE,CmpRaiseSelfHealWarning)
|
|
#pragma alloc_text(PAGE,CmpRaiseSelfHealWarningForSystemHives)
|
|
#pragma alloc_text(PAGE,CmpRaiseSelfHealWarningWorker)
|
|
#endif
|
|
|
|
//
|
|
// Registry control values
|
|
//
|
|
#define CM_DEFAULT_RATIO (3)
|
|
#define CM_LIMIT_RATIO(x) ((x / 10) * 8)
|
|
#define CM_MINIMUM_GLOBAL_QUOTA (16 *1024 * 1024)
|
|
|
|
//
|
|
// Percent of used registry quota that triggers a hard error
|
|
// warning popup.
|
|
//
|
|
#define CM_REGISTRY_WARNING_LEVEL (95)
|
|
|
|
//
|
|
// System hive hard quota limit
|
|
//
|
|
// For an x86 3GB system we set the limit at 12MB for now. Needs some MM changes before we
|
|
// bump this up.
|
|
// For an x86 non-3GB system, we set the limit at 1/4 of physical memory
|
|
// For IA-64 we set the limit at 32MB
|
|
//
|
|
|
|
#define _200MB (200 *1024 * 1024)
|
|
|
|
#if defined(_X86_)
|
|
#define CM_SYSTEM_HIVE_LIMIT_SIZE (MmVirtualBias ? (12 * 1024 * 1024) : (min(MmNumberOfPhysicalPages / 4, _200MB >> PAGE_SHIFT) * PAGE_SIZE))
|
|
#else
|
|
#define CM_SYSTEM_HIVE_LIMIT_SIZE (32 * 1024 * 1024)
|
|
#endif
|
|
|
|
#define CM_SYSTEM_HIVE_WARNING_SIZE ((CM_SYSTEM_HIVE_LIMIT_SIZE*9)/10)
|
|
|
|
|
|
extern ULONG CmRegistrySizeLimit;
|
|
extern ULONG CmRegistrySizeLimitLength;
|
|
extern ULONG CmRegistrySizeLimitType;
|
|
|
|
extern ULONG MmSizeOfPagedPoolInBytes;
|
|
|
|
//
|
|
// Maximum number of bytes of Global Quota the registry may use.
|
|
// Set to largest positive number for use in boot. Will be set down
|
|
// based on pool and explicit registry values.
|
|
//
|
|
extern ULONG CmpGlobalQuota;
|
|
extern ULONG CmpGlobalQuotaAllowed;
|
|
|
|
//
|
|
// Mark that will trigger the low-on-quota popup
|
|
//
|
|
extern ULONG CmpGlobalQuotaWarning;
|
|
|
|
//
|
|
// Indicate whether the popup has been triggered yet or not.
|
|
//
|
|
extern BOOLEAN CmpQuotaWarningPopupDisplayed;
|
|
|
|
extern BOOLEAN CmpSystemQuotaWarningPopupDisplayed;
|
|
|
|
//
|
|
// GQ actually in use
|
|
//
|
|
extern ULONG CmpGlobalQuotaUsed;
|
|
|
|
extern HIVE_LIST_ENTRY CmpMachineHiveList[];
|
|
|
|
VOID
|
|
CmQueryRegistryQuotaInformation(
|
|
IN PSYSTEM_REGISTRY_QUOTA_INFORMATION RegistryQuotaInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the registry quota information
|
|
|
|
Arguments:
|
|
|
|
RegistryQuotaInformation - Supplies pointer to buffer that will return
|
|
the registry quota information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RegistryQuotaInformation->RegistryQuotaAllowed = CmpGlobalQuota;
|
|
RegistryQuotaInformation->RegistryQuotaUsed = CmpGlobalQuotaUsed;
|
|
RegistryQuotaInformation->PagedPoolSize = MmSizeOfPagedPoolInBytes;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmSetRegistryQuotaInformation(
|
|
IN PSYSTEM_REGISTRY_QUOTA_INFORMATION RegistryQuotaInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the registry quota information. The caller is assumed to have
|
|
completed the necessary security checks already.
|
|
|
|
Arguments:
|
|
|
|
RegistryQuotaInformation - Supplies pointer to buffer that provides
|
|
the new registry quota information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
CmpGlobalQuota = RegistryQuotaInformation->RegistryQuotaAllowed;
|
|
|
|
//
|
|
// Sanity checks against insane values
|
|
//
|
|
if (CmpGlobalQuota > CM_WRAP_LIMIT) {
|
|
CmpGlobalQuota = CM_WRAP_LIMIT;
|
|
}
|
|
if (CmpGlobalQuota < CM_MINIMUM_GLOBAL_QUOTA) {
|
|
CmpGlobalQuota = CM_MINIMUM_GLOBAL_QUOTA;
|
|
}
|
|
|
|
//
|
|
// Recompute the warning level
|
|
//
|
|
CmpGlobalQuotaWarning = CM_REGISTRY_WARNING_LEVEL * (CmpGlobalQuota / 100);
|
|
|
|
CmpGlobalQuotaAllowed = CmpGlobalQuota;
|
|
}
|
|
|
|
VOID
|
|
CmpQuotaWarningWorker(
|
|
IN PVOID WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays hard error popup that indicates the registry quota is
|
|
running out.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Supplies pointer to the work item. This routine will
|
|
free the work item.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Response;
|
|
|
|
ExFreePool(WorkItem);
|
|
|
|
Status = ExRaiseHardError(STATUS_REGISTRY_QUOTA_LIMIT,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OptionOk,
|
|
&Response);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpClaimGlobalQuota(
|
|
IN ULONG Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If CmpGlobalQuotaUsed + Size >= CmpGlobalQuotaAllowed, return
|
|
false. Otherwise, increment CmpGlobalQuotaUsed, in effect claiming
|
|
the requested GlobalQuota.
|
|
|
|
Arguments:
|
|
|
|
Size - number of bytes of GlobalQuota caller wants to claim
|
|
|
|
Return Value:
|
|
|
|
TRUE - Claim succeeded, and has been counted in Used GQ
|
|
|
|
FALSE - Claim failed, nothing counted in GQ.
|
|
|
|
--*/
|
|
{
|
|
#if 0
|
|
//
|
|
// We shouldn't come to this, unless we have leaks;
|
|
// There is no quota anymore, remember?
|
|
//
|
|
LONG available;
|
|
PWORK_QUEUE_ITEM WorkItem;
|
|
|
|
//
|
|
// compute available space, then see if size <. This prevents overflows.
|
|
// Note that this must be signed. Since quota is not enforced until logon,
|
|
// it is possible for the available bytes to be negative.
|
|
//
|
|
|
|
available = (LONG)CmpGlobalQuotaAllowed - (LONG)CmpGlobalQuotaUsed;
|
|
|
|
if ((LONG)Size < available) {
|
|
CmpGlobalQuotaUsed += Size;
|
|
if ((CmpGlobalQuotaUsed > CmpGlobalQuotaWarning) &&
|
|
(!CmpQuotaWarningPopupDisplayed) &&
|
|
(ExReadyForErrors)) {
|
|
|
|
|
|
//
|
|
// Queue work item to display popup
|
|
//
|
|
WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
|
|
if (WorkItem != NULL) {
|
|
|
|
CmpQuotaWarningPopupDisplayed = TRUE;
|
|
ExInitializeWorkItem(WorkItem,
|
|
CmpQuotaWarningWorker,
|
|
WorkItem);
|
|
ExQueueWorkItem(WorkItem, DelayedWorkQueue);
|
|
}
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#endif //0
|
|
|
|
CmpGlobalQuotaUsed += Size;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpReleaseGlobalQuota(
|
|
IN ULONG Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If Size <= CmpGlobalQuotaUsed, then decrement it. Else BugCheck.
|
|
|
|
Arguments:
|
|
|
|
Size - number of bytes of GlobalQuota caller wants to release
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
if (Size > CmpGlobalQuotaUsed) {
|
|
CM_BUGCHECK(REGISTRY_ERROR,QUOTA_ERROR,1,0,0);
|
|
}
|
|
|
|
CmpGlobalQuotaUsed -= Size;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpComputeGlobalQuotaAllowed(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compute CmpGlobalQuota based on:
|
|
(a) Size of paged pool
|
|
(b) Explicit user registry commands to set registry GQ
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PagedLimit;
|
|
|
|
PagedLimit = CM_LIMIT_RATIO(MmSizeOfPagedPoolInBytes);
|
|
|
|
if ((CmRegistrySizeLimitLength != 4) ||
|
|
(CmRegistrySizeLimitType != REG_DWORD) ||
|
|
(CmRegistrySizeLimit == 0))
|
|
{
|
|
//
|
|
// If no value at all, or value of wrong type, or set to
|
|
// zero, use internally computed default
|
|
//
|
|
CmpGlobalQuota = MmSizeOfPagedPoolInBytes / CM_DEFAULT_RATIO;
|
|
|
|
} else if (CmRegistrySizeLimit >= PagedLimit) {
|
|
//
|
|
// If more than computed upper bound, use computed upper bound
|
|
//
|
|
CmpGlobalQuota = PagedLimit;
|
|
|
|
} else {
|
|
//
|
|
// Use the set size
|
|
//
|
|
CmpGlobalQuota = CmRegistrySizeLimit;
|
|
|
|
}
|
|
|
|
if (CmpGlobalQuota > CM_WRAP_LIMIT) {
|
|
CmpGlobalQuota = CM_WRAP_LIMIT;
|
|
}
|
|
if (CmpGlobalQuota < CM_MINIMUM_GLOBAL_QUOTA) {
|
|
CmpGlobalQuota = CM_MINIMUM_GLOBAL_QUOTA;
|
|
}
|
|
|
|
CmpGlobalQuotaWarning = CM_REGISTRY_WARNING_LEVEL * (CmpGlobalQuota / 100);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpSetGlobalQuotaAllowed(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enables registry quota
|
|
|
|
NOTE: Do NOT put this in init segment, we call it after
|
|
that code has been freed!
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
CmpGlobalQuotaAllowed = CmpGlobalQuota;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpCanGrowSystemHive(
|
|
IN PHHIVE Hive,
|
|
IN ULONG NewLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if the system hive is allowed to grow with the specified amount
|
|
of data (using the hard quota limit on the system hive)
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
PCMHIVE CmHive;
|
|
PWORK_QUEUE_ITEM WorkItem;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(Hive,CMHIVE,Hive);
|
|
|
|
if( CmHive != CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive ) {
|
|
//
|
|
// not the system hive, bail out
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
// account for the header.
|
|
NewLength += HBLOCK_SIZE;
|
|
if( NewLength > CM_SYSTEM_HIVE_LIMIT_SIZE ) {
|
|
//
|
|
// this is bad; we may not be able to boot next time !!!
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if( (NewLength > CM_SYSTEM_HIVE_WARNING_SIZE) &&
|
|
(!CmpSystemQuotaWarningPopupDisplayed) &&
|
|
(ExReadyForErrors)
|
|
) {
|
|
//
|
|
// we're above the warning level, queue work item to display popup
|
|
//
|
|
WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
|
|
if (WorkItem != NULL) {
|
|
|
|
CmpSystemQuotaWarningPopupDisplayed = TRUE;
|
|
ExInitializeWorkItem(WorkItem,
|
|
CmpSystemQuotaWarningWorker,
|
|
WorkItem);
|
|
ExQueueWorkItem(WorkItem, DelayedWorkQueue);
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpSystemQuotaWarningWorker(
|
|
IN PVOID WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays hard error popup that indicates the hard quota limit
|
|
on the system hive is running out.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Supplies pointer to the work item. This routine will
|
|
free the work item.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Response;
|
|
|
|
ExFreePool(WorkItem);
|
|
|
|
Status = ExRaiseHardError(STATUS_REGISTRY_QUOTA_LIMIT,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OptionOk,
|
|
&Response);
|
|
}
|
|
|
|
//
|
|
// Pnp private API
|
|
//
|
|
ULONG CmpSystemHiveHysteresisLow = 0;
|
|
ULONG CmpSystemHiveHysteresisHigh = 0;
|
|
PVOID CmpSystemHiveHysteresisContext = NULL;
|
|
PCM_HYSTERESIS_CALLBACK CmpSystemHiveHysteresisCallback = NULL;
|
|
ULONG CmpSystemHiveHysteresisHitRatio = 0;
|
|
BOOLEAN CmpSystemHiveHysteresisLowSeen = FALSE;
|
|
BOOLEAN CmpSystemHiveHysteresisHighSeen = FALSE;
|
|
|
|
VOID
|
|
CmpSystemHiveHysteresisWorker(
|
|
IN PVOID WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls the hysteresis callback
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Supplies pointer to the work item. This routine will
|
|
free the work item.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCM_HYSTERESIS_CALLBACK Callback;
|
|
|
|
ExFreePool(WorkItem);
|
|
|
|
Callback = CmpSystemHiveHysteresisCallback;
|
|
|
|
if( Callback ) {
|
|
(*Callback)(CmpSystemHiveHysteresisContext,CmpSystemHiveHysteresisHitRatio);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpUpdateSystemHiveHysteresis( PHHIVE Hive,
|
|
ULONG NewLength,
|
|
ULONG OldLength
|
|
)
|
|
{
|
|
PCMHIVE CmHive;
|
|
PWORK_QUEUE_ITEM WorkItem;
|
|
ULONG CurrentRatio;
|
|
BOOLEAN DoWorkItem = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(Hive,CMHIVE,Hive);
|
|
|
|
if( (!CmpSystemHiveHysteresisCallback) || (CmHive != CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive) ) {
|
|
//
|
|
// not the system hive, bail out
|
|
//
|
|
return;
|
|
}
|
|
|
|
ASSERT( NewLength != OldLength );
|
|
|
|
//
|
|
// compute current ratio; acount for the header first
|
|
//
|
|
CurrentRatio = NewLength + HBLOCK_SIZE;
|
|
CurrentRatio *= 100;
|
|
CurrentRatio /= CM_SYSTEM_HIVE_LIMIT_SIZE;
|
|
|
|
if( NewLength > OldLength ) {
|
|
//
|
|
// hive is growing
|
|
//
|
|
if( (CmpSystemHiveHysteresisHighSeen == FALSE) && (CurrentRatio > CmpSystemHiveHysteresisHigh) ) {
|
|
//
|
|
// we reached high; see if low has already been hit and queue work item
|
|
//
|
|
CmpSystemHiveHysteresisHighSeen = TRUE;
|
|
if( TRUE == CmpSystemHiveHysteresisLowSeen ) {
|
|
//
|
|
// low to high; queue workitem
|
|
//
|
|
CmpSystemHiveHysteresisHitRatio = CurrentRatio;
|
|
DoWorkItem = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// hive is shrinking
|
|
//
|
|
if( (FALSE == CmpSystemHiveHysteresisLowSeen) && (CurrentRatio < CmpSystemHiveHysteresisLow ) ) {
|
|
//
|
|
// we reached low; see if low has been hit and queue work item
|
|
//
|
|
CmpSystemHiveHysteresisLowSeen = TRUE;
|
|
if( TRUE == CmpSystemHiveHysteresisHighSeen ) {
|
|
//
|
|
// high to low; queue workitem
|
|
//
|
|
CmpSystemHiveHysteresisHitRatio = CurrentRatio;
|
|
DoWorkItem = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( DoWorkItem ) {
|
|
ASSERT( CmpSystemHiveHysteresisLowSeen && CmpSystemHiveHysteresisHighSeen );
|
|
|
|
WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
|
|
if (WorkItem != NULL) {
|
|
|
|
ExInitializeWorkItem(WorkItem,
|
|
CmpSystemHiveHysteresisWorker,
|
|
WorkItem);
|
|
ExQueueWorkItem(WorkItem, DelayedWorkQueue);
|
|
}
|
|
//
|
|
// reset state so we can fire again later
|
|
//
|
|
CmpSystemHiveHysteresisLowSeen = FALSE;
|
|
CmpSystemHiveHysteresisHighSeen = FALSE;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
CmRegisterSystemHiveLimitCallback(
|
|
ULONG Low,
|
|
ULONG High,
|
|
PVOID Ref,
|
|
PCM_HYSTERESIS_CALLBACK Callback
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers a hysteresis for the system hive limit ratio.
|
|
We will call the callback :
|
|
|
|
a. the system hive goes above High from below Low
|
|
b. the system hive goes below Low from above High
|
|
|
|
Arguments:
|
|
|
|
Low, High - specifies the hysteresis
|
|
|
|
Ref - Context to give back to the callback
|
|
|
|
Callback - callback routine.
|
|
|
|
Return Value:
|
|
|
|
current ratio 0 - 100
|
|
|
|
--*/
|
|
{
|
|
ULONG Length;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive ) {
|
|
Length = CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive->Hive.BaseBlock->Length + HBLOCK_SIZE;
|
|
|
|
Length *= 100;
|
|
Length /= CM_SYSTEM_HIVE_LIMIT_SIZE;
|
|
} else {
|
|
Length = 0;
|
|
}
|
|
|
|
//
|
|
// allow only one call per system uptime.
|
|
//
|
|
if( CmpSystemHiveHysteresisCallback == NULL ) {
|
|
CmpSystemHiveHysteresisLow = Low;
|
|
CmpSystemHiveHysteresisHigh = High;
|
|
CmpSystemHiveHysteresisContext = Ref;
|
|
CmpSystemHiveHysteresisCallback = Callback;
|
|
//
|
|
// set state vars
|
|
//
|
|
if( Length <= Low ) {
|
|
CmpSystemHiveHysteresisLowSeen = TRUE;
|
|
} else {
|
|
CmpSystemHiveHysteresisLowSeen = FALSE;
|
|
}
|
|
if( Length >= High) {
|
|
CmpSystemHiveHysteresisHighSeen = TRUE;
|
|
} else {
|
|
CmpSystemHiveHysteresisHighSeen = FALSE;
|
|
}
|
|
}
|
|
return Length;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpHysteresisTest(PVOID Ref, ULONG Level)
|
|
{
|
|
UNREFERENCED_PARAMETER (Ref);
|
|
|
|
DbgPrint("CmpHysteresisTest called with level = %lu \n",Level);
|
|
}
|
|
|
|
LIST_ENTRY CmpSelfHealQueueListHead;
|
|
FAST_MUTEX CmpSelfHealQueueLock;
|
|
BOOLEAN CmpSelfHealWorkerActive = FALSE;
|
|
|
|
#define LOCK_SELF_HEAL_QUEUE() ExAcquireFastMutex(&CmpSelfHealQueueLock)
|
|
#define UNLOCK_SELF_HEAL_QUEUE() ExReleaseFastMutex(&CmpSelfHealQueueLock)
|
|
|
|
typedef struct {
|
|
PWORK_QUEUE_ITEM WorkItem;
|
|
LIST_ENTRY SelfHealQueueListEntry;
|
|
UNICODE_STRING HiveName;
|
|
//
|
|
// variable length; name goes here
|
|
//
|
|
} CM_SELF_HEAL_WORK_ITEM_PARAMETER, *PCM_SELF_HEAL_WORK_ITEM_PARAMETER;
|
|
|
|
VOID
|
|
CmpRaiseSelfHealWarningWorker(
|
|
IN PVOID Arg
|
|
)
|
|
{
|
|
PVOID ErrorParameters;
|
|
ULONG ErrorResponse;
|
|
PCM_SELF_HEAL_WORK_ITEM_PARAMETER Param;
|
|
|
|
Param = (PCM_SELF_HEAL_WORK_ITEM_PARAMETER)Arg;
|
|
ErrorParameters = &(Param->HiveName);
|
|
ExRaiseHardError(
|
|
STATUS_REGISTRY_HIVE_RECOVERED,
|
|
1,
|
|
1,
|
|
(PULONG_PTR)&ErrorParameters,
|
|
OptionOk,
|
|
&ErrorResponse
|
|
);
|
|
|
|
//
|
|
// free what we have allocated
|
|
//
|
|
ExFreePool(Param->WorkItem);
|
|
ExFreePool(Param);
|
|
|
|
//
|
|
// see if there are other self heal warnings to be posted.
|
|
//
|
|
LOCK_SELF_HEAL_QUEUE();
|
|
CmpSelfHealWorkerActive = FALSE;
|
|
if( IsListEmpty(&CmpSelfHealQueueListHead) == FALSE ) {
|
|
//
|
|
// remove head and queue it.
|
|
//
|
|
Param = (PCM_SELF_HEAL_WORK_ITEM_PARAMETER)RemoveHeadList(&CmpSelfHealQueueListHead);
|
|
Param = CONTAINING_RECORD(
|
|
Param,
|
|
CM_SELF_HEAL_WORK_ITEM_PARAMETER,
|
|
SelfHealQueueListEntry
|
|
);
|
|
ExQueueWorkItem(Param->WorkItem, DelayedWorkQueue);
|
|
CmpSelfHealWorkerActive = TRUE;
|
|
}
|
|
UNLOCK_SELF_HEAL_QUEUE();
|
|
}
|
|
|
|
VOID
|
|
CmpRaiseSelfHealWarning(
|
|
IN PUNICODE_STRING HiveName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Raise a hard error informing the use the specified hive has been self healed and
|
|
it might not be entirely consitent
|
|
|
|
Arguments:
|
|
|
|
Parameter - the hive name.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PCM_SELF_HEAL_WORK_ITEM_PARAMETER Param;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// we're above the warning level, queue work item to display popup
|
|
//
|
|
Param = ExAllocatePool(NonPagedPool, sizeof(CM_SELF_HEAL_WORK_ITEM_PARAMETER) + HiveName->Length);
|
|
if( Param ) {
|
|
Param->WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
|
|
if(Param->WorkItem != NULL) {
|
|
Param->HiveName.Length = Param->HiveName.MaximumLength = HiveName->Length;
|
|
Param->HiveName.Buffer = (PWSTR)(((PUCHAR)Param) + sizeof(CM_SELF_HEAL_WORK_ITEM_PARAMETER));
|
|
RtlCopyMemory(Param->HiveName.Buffer,HiveName->Buffer,HiveName->Length);
|
|
ExInitializeWorkItem(Param->WorkItem,
|
|
CmpRaiseSelfHealWarningWorker,
|
|
Param);
|
|
LOCK_SELF_HEAL_QUEUE();
|
|
if( !CmpSelfHealWorkerActive ) {
|
|
//
|
|
// no work item currently; ok to queue one.
|
|
//
|
|
ExQueueWorkItem(Param->WorkItem, DelayedWorkQueue);
|
|
CmpSelfHealWorkerActive = TRUE;
|
|
} else {
|
|
//
|
|
// add it to the end of the list. It'll be picked up when the current work item
|
|
// completes
|
|
//
|
|
InsertTailList(
|
|
&CmpSelfHealQueueListHead,
|
|
&(Param->SelfHealQueueListEntry)
|
|
);
|
|
}
|
|
UNLOCK_SELF_HEAL_QUEUE();
|
|
} else {
|
|
ExFreePool(Param);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CmpRaiseSelfHealWarningForSystemHives( )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walks the system hivelist and raises a hard error in the event one of the hives has been self healed.
|
|
|
|
Intended to be called after controlset has been saved, from inside NtInitializeRegistry
|
|
(i.e. we have an UI available so it will not stop the machine).
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
UNICODE_STRING Name;
|
|
|
|
PAGED_CODE();
|
|
|
|
for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++) {
|
|
if( !(CmpMachineHiveList[i].HHiveFlags & HIVE_VOLATILE) && (((PHHIVE)(CmpMachineHiveList[i].CmHive2))->BaseBlock->BootType & HBOOT_SELFHEAL) ) {
|
|
RtlInitUnicodeString(
|
|
&Name,
|
|
CmpMachineHiveList[i].Name
|
|
);
|
|
CmpRaiseSelfHealWarning( &Name );
|
|
}
|
|
}
|
|
|
|
}
|