364 lines
7.7 KiB
C
364 lines
7.7 KiB
C
/*++
|
||
|
||
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:
|
||
|
||
--*/
|
||
|
||
#include "cmp.h"
|
||
|
||
#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(INIT,CmpComputeGlobalQuotaAllowed)
|
||
#endif
|
||
|
||
//
|
||
// Registry control values
|
||
//
|
||
#define CM_DEFAULT_RATIO (4)
|
||
#define CM_LIMIT_RATIO(x) ((x / 10) * 8)
|
||
#define CM_MINIMUM_GLOBAL_QUOTA (4*1024 * 1024)
|
||
|
||
//
|
||
// Percent of used registry quota that triggers a hard error
|
||
// warning popup.
|
||
//
|
||
#define CM_REGISTRY_WARNING_LEVEL (95)
|
||
|
||
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;
|
||
|
||
//
|
||
// GQ actually in use
|
||
//
|
||
extern ULONG CmpGlobalQuotaUsed;
|
||
|
||
|
||
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;
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
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) {
|
||
KeBugCheckEx(REGISTRY_ERROR,2,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);
|
||
|
||
ASSERT(PagedLimit > CM_MINIMUM_GLOBAL_QUOTA);
|
||
|
||
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 < CM_MINIMUM_GLOBAL_QUOTA) {
|
||
//
|
||
// If less than defined lower bound, use defined lower bound
|
||
//
|
||
CmpGlobalQuota = CM_MINIMUM_GLOBAL_QUOTA;
|
||
|
||
} 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;
|
||
}
|
||
|
||
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;
|
||
}
|