|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
group.c
Abstract:
This module contains Group ID managment routines.
Group IDs identify an AFD_GROUP_ENTRY structure in a lookup table. Each AFD_GROUP_ENTRY contains a reference count and a type (either GroupTypeConstrained or GroupTypeUnconstrained). Free group IDs are linked together in a doubly-linked list. As group IDs are allocated, they are removed from this list. Once the free list becomes empty, the lookup table is grown appropriately.
Author:
Keith Moore (keithmo) 06-Jun-1996
Revision History:
--*/
#include "afdp.h"
//
// Private constants.
//
#define AFD_GROUP_TABLE_GROWTH 32 // entries
//
// Private types.
//
typedef struct _AFD_GROUP_ENTRY { union { LIST_ENTRY ListEntry; struct { AFD_GROUP_TYPE GroupType; LONG ReferenceCount; }; }; } AFD_GROUP_ENTRY, *PAFD_GROUP_ENTRY;
//
// Private globals.
//
PERESOURCE AfdGroupTableResource; PAFD_GROUP_ENTRY AfdGroupTable; LIST_ENTRY AfdFreeGroupList; LONG AfdGroupTableSize;
//
// Private functions.
//
PAFD_GROUP_ENTRY AfdMapGroupToEntry( IN LONG Group );
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, AfdInitializeGroup )
#pragma alloc_text( PAGE, AfdTerminateGroup )
#pragma alloc_text( PAGE, AfdReferenceGroup )
#pragma alloc_text( PAGE, AfdDereferenceGroup )
#pragma alloc_text( PAGE, AfdGetGroup )
#endif
BOOLEAN AfdInitializeGroup( VOID )
/*++
Routine Description:
Initializes any globals necessary for the group ID package.
Return Value:
BOOLEAN - TRUE if successful, FALSE otherwise.
--*/
{
//
// Initialize the group globals.
//
AfdGroupTableResource = AFD_ALLOCATE_POOL_PRIORITY( NonPagedPool, sizeof(*AfdGroupTableResource), AFD_RESOURCE_POOL_TAG, HighPoolPriority );
if( AfdGroupTableResource == NULL ) {
return FALSE;
}
ExInitializeResourceLite( AfdGroupTableResource );
AfdGroupTable = NULL; InitializeListHead( &AfdFreeGroupList ); AfdGroupTableSize = 0;
return TRUE;
} // AfdInitializeGroup
VOID AfdTerminateGroup( VOID )
/*++
Routine Description:
Destroys any globals created for the group ID package.
--*/
{
if( AfdGroupTableResource != NULL ) {
ExDeleteResourceLite( AfdGroupTableResource );
AFD_FREE_POOL( AfdGroupTableResource, AFD_RESOURCE_POOL_TAG );
AfdGroupTableResource = NULL;
}
if( AfdGroupTable != NULL ) {
AFD_FREE_POOL( AfdGroupTable, AFD_GROUP_POOL_TAG );
AfdGroupTable = NULL;
}
InitializeListHead( &AfdFreeGroupList ); AfdGroupTableSize = 0;
} // AfdTerminateGroup
BOOLEAN AfdReferenceGroup( IN LONG Group, OUT PAFD_GROUP_TYPE GroupType )
/*++
Routine Description:
Bumps the reference count associated with the given group ID.
Arguments:
Group - The group ID to reference.
GroupType - Returns the type of the group.
Returns:
BOOLEAN - TRUE if the group ID was valid, FALSE otherwise.
--*/
{
PAFD_GROUP_ENTRY groupEntry; AFD_GROUP_TYPE groupType;
groupEntry = AfdMapGroupToEntry( Group );
if( groupEntry != NULL ) {
groupType = groupEntry->GroupType;
if( groupType == GroupTypeConstrained || groupType == GroupTypeUnconstrained ) {
groupEntry->ReferenceCount++; *GroupType = groupType;
} else {
groupEntry = NULL;
}
ExReleaseResourceLite( AfdGroupTableResource ); KeLeaveCriticalRegion ();
}
return (BOOLEAN)( groupEntry != NULL );
} // AfdReferenceGroup
BOOLEAN AfdDereferenceGroup( IN LONG Group )
/*++
Routine Description:
Decrements the reference count associated with the given group ID. If the ref count drops to zero, the group ID is freed.
Arguments:
Group - The group ID to dereference.
Returns:
BOOLEAN - TRUE if the group ID was valid, FALSE otherwise.
--*/
{
PAFD_GROUP_ENTRY groupEntry; AFD_GROUP_TYPE groupType;
groupEntry = AfdMapGroupToEntry( Group );
if( groupEntry != NULL ) {
groupType = groupEntry->GroupType;
if( groupType == GroupTypeConstrained || groupType == GroupTypeUnconstrained ) {
ASSERT( groupEntry->ReferenceCount > 0 ); groupEntry->ReferenceCount--;
if( groupEntry->ReferenceCount == 0 ) {
InsertTailList( &AfdFreeGroupList, &groupEntry->ListEntry );
}
} else {
groupEntry = NULL;
}
ExReleaseResourceLite( AfdGroupTableResource ); KeLeaveCriticalRegion ();
}
return (BOOLEAN)( groupEntry != NULL );
} // AfdDereferenceGroup
BOOLEAN AfdGetGroup( IN OUT PLONG Group, OUT PAFD_GROUP_TYPE GroupType )
/*++
Routine Description:
Examines the incoming group. If is zero, then nothing is done. If it is SG_CONSTRAINED_GROUP, then a new constrained group ID is created. If it is SG_UNCONSTRAINED_GROUP, then a new unconstrained group ID is created. Otherwise, it must identify an existing group, so that group is referenced.
Arguments:
Group - Points to the group ID to examine/modify.
GroupType - Returns the type of the group.
Return Value:
BOOLEAN - TRUE if successful, FALSE otherwise.
--*/
{
LONG groupValue; PAFD_GROUP_ENTRY groupEntry; PAFD_GROUP_ENTRY newGroupTable; LONG newGroupTableSize; LONG i; PLIST_ENTRY listEntry;
groupValue = *Group;
//
// Zero means "no group", so just ignore it.
//
if( groupValue == 0 ) {
*GroupType = GroupTypeNeither; return TRUE;
}
//
// If we're being asked to create a new group, do it.
//
if( groupValue == SG_CONSTRAINED_GROUP || groupValue == SG_UNCONSTRAINED_GROUP ) {
//
// Lock the table.
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdGroupTableResource, TRUE );
//
// See if there's room at the inn.
//
if( IsListEmpty( &AfdFreeGroupList ) ) {
//
// No room, we'll need to create/expand the table.
//
newGroupTableSize = AfdGroupTableSize + AFD_GROUP_TABLE_GROWTH;
newGroupTable = AFD_ALLOCATE_POOL( PagedPool, newGroupTableSize * sizeof(AFD_GROUP_ENTRY), AFD_GROUP_POOL_TAG );
if( newGroupTable == NULL ) {
ExReleaseResourceLite( AfdGroupTableResource ); KeLeaveCriticalRegion (); return FALSE;
}
if( AfdGroupTable == NULL ) {
//
// This is the initial table allocation, so reserve the
// first three entries (0, SG_UNCONSTRAINED_GROUP, and
// SG_CONSTRAINED_GROUP).
//
for( ; AfdGroupTableSize <= SG_CONSTRAINED_GROUP || AfdGroupTableSize <= SG_UNCONSTRAINED_GROUP ; AfdGroupTableSize++ ) {
newGroupTable[AfdGroupTableSize].ReferenceCount = 0; newGroupTable[AfdGroupTableSize].GroupType = GroupTypeNeither;
}
} else {
//
// Copy the old table into the new table, then free the
// old table.
//
RtlCopyMemory( newGroupTable, AfdGroupTable, AfdGroupTableSize * sizeof(AFD_GROUP_ENTRY) );
AFD_FREE_POOL( AfdGroupTable, AFD_GROUP_POOL_TAG );
}
//
// Add the new entries to the free list.
//
for( i = newGroupTableSize - 1 ; i >= AfdGroupTableSize ; i-- ) {
InsertHeadList( &AfdFreeGroupList, &newGroupTable[i].ListEntry );
}
AfdGroupTable = newGroupTable; AfdGroupTableSize = newGroupTableSize;
}
//
// Pull the next free entry off the list.
//
ASSERT( !IsListEmpty( &AfdFreeGroupList ) );
listEntry = RemoveHeadList( &AfdFreeGroupList );
groupEntry = CONTAINING_RECORD( listEntry, AFD_GROUP_ENTRY, ListEntry );
groupEntry->ReferenceCount = 1; groupEntry->GroupType = (AFD_GROUP_TYPE)groupValue;
*Group = (LONG)( groupEntry - AfdGroupTable ); *GroupType = groupEntry->GroupType;
ExReleaseResourceLite( AfdGroupTableResource ); KeLeaveCriticalRegion (); return TRUE;
}
//
// Otherwise, just reference the group.
//
return AfdReferenceGroup( groupValue, GroupType );
} // AfdGetGroup
PAFD_GROUP_ENTRY AfdMapGroupToEntry( IN LONG Group )
/*++
Routine Description:
Maps the given group ID to the corresponding AFD_GROUP_ENTRY structure.
N.B. This routine returns with AfdGroupTableResource held if successful.
Arguments:
Group - The group ID to map.
Return Value:
PAFD_GROUP_ENTRY - The entry corresponding to the group ID if successful, NULL otherwise.
--*/
{
PAFD_GROUP_ENTRY groupEntry;
//
// Lock the table.
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdGroupTableResource, TRUE );
//
// Validate the group ID.
//
if( Group > 0 && Group < AfdGroupTableSize ) {
groupEntry = AfdGroupTable + Group;
//
// The group ID is within legal range. Ensure it's in use.
// In the AFD_GROUP_ENTRY structure, the GroupType field is
// overlayed with ListEntry.Flink due to the internal union.
// We can use this knowledge to quickly validate that this
// entry is in use.
//
if( groupEntry->GroupType == GroupTypeConstrained || groupEntry->GroupType == GroupTypeUnconstrained ) {
return groupEntry;
}
}
//
// Invalid group ID, fail it.
//
ExReleaseResourceLite( AfdGroupTableResource ); KeLeaveCriticalRegion (); return NULL;
} // AfdMapGroupToEntry
|