mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
- Object Manager Improvement Patch 1/3:
- Re-implement delayed object deletion by using an optimized Object Reaper based on OBJECT_HEADER->NextToFree. Thanks to Thomas for the algorithm. - Refactor object deletion into two operations: Removal and de-allocation (this is needed for failure during allocation, which we don't do yet). - BugFixes: * After freeing an object header structure, also clear the pointer so we don't attempt it again. * Clear the handle database if there is one * Make sure the create info flag is set before deleting captured attributes. * Use the allocation pool tag when de-allocating. * Use OBJECT_TYPE accounting for tracking the numbef of objects. * Remove the object from it's typelist if it has creator information. * Call the security procedure to delete the security descriptor. svn path=/trunk/; revision=22098
This commit is contained in:
parent
8a8feb3718
commit
0817ba9198
4 changed files with 166 additions and 139 deletions
|
@ -416,7 +416,7 @@ typedef struct _OBJECT_HEADER
|
|||
union
|
||||
{
|
||||
LONG HandleCount;
|
||||
PVOID NextToFree;
|
||||
volatile PVOID NextToFree;
|
||||
};
|
||||
POBJECT_TYPE Type;
|
||||
UCHAR NameInfoOffset;
|
||||
|
|
|
@ -30,6 +30,8 @@ extern POBJECT_TYPE ObTypeObjectType;
|
|||
extern POBJECT_DIRECTORY NameSpaceRoot;
|
||||
extern POBJECT_DIRECTORY ObpTypeDirectoryObject;
|
||||
extern PHANDLE_TABLE ObpKernelHandleTable;
|
||||
extern WORK_QUEUE_ITEM ObpReaperWorkItem;
|
||||
extern volatile PVOID ObpReaperList;
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
|
@ -157,10 +159,16 @@ VOID
|
|||
STDCALL
|
||||
ObKillProcess(PEPROCESS Process);
|
||||
|
||||
NTSTATUS
|
||||
ObpDeleteObjectDpcLevel(
|
||||
IN POBJECT_HEADER ObjectHeader,
|
||||
IN LONG OldPointerCount
|
||||
VOID
|
||||
FASTCALL
|
||||
ObpDeleteObject(
|
||||
IN PVOID Object
|
||||
);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
ObpReapObject(
|
||||
IN PVOID Unused
|
||||
);
|
||||
|
||||
/* Security descriptor cache functions */
|
||||
|
|
|
@ -21,59 +21,33 @@ extern ULONG NtGlobalFlag;
|
|||
|
||||
POBJECT_TYPE ObTypeObjectType = NULL;
|
||||
KEVENT ObpDefaultObject;
|
||||
|
||||
typedef struct _RETENTION_CHECK_PARAMS
|
||||
{
|
||||
WORK_QUEUE_ITEM WorkItem;
|
||||
POBJECT_HEADER ObjectHeader;
|
||||
} RETENTION_CHECK_PARAMS, *PRETENTION_CHECK_PARAMS;
|
||||
WORK_QUEUE_ITEM ObpReaperWorkItem;
|
||||
volatile PVOID ObpReaperList;
|
||||
|
||||
/* PRIVATE FUNCTIONS *********************************************************/
|
||||
|
||||
static NTSTATUS
|
||||
ObpDeleteObject(POBJECT_HEADER Header)
|
||||
VOID
|
||||
FASTCALL
|
||||
ObpDeallocateObject(IN PVOID Object)
|
||||
{
|
||||
PVOID HeaderLocation = Header;
|
||||
PVOID HeaderLocation;
|
||||
POBJECT_HEADER Header;
|
||||
POBJECT_TYPE ObjectType;
|
||||
POBJECT_HEADER_HANDLE_INFO HandleInfo;
|
||||
POBJECT_HEADER_NAME_INFO NameInfo;
|
||||
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
|
||||
PAGED_CODE();
|
||||
|
||||
DPRINT("ObpDeleteObject(Header %p)\n", Header);
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
{
|
||||
DPRINT("ObpDeleteObject called at an unsupported IRQL. Use ObpDeleteObjectDpcLevel instead.\n");
|
||||
KEBUGCHECK(0);
|
||||
}
|
||||
|
||||
if (Header->Type != NULL &&
|
||||
Header->Type->TypeInfo.DeleteProcedure != NULL)
|
||||
{
|
||||
Header->Type->TypeInfo.DeleteProcedure(&Header->Body);
|
||||
}
|
||||
|
||||
if (Header->SecurityDescriptor != NULL)
|
||||
{
|
||||
ObpRemoveSecurityDescriptor(Header->SecurityDescriptor);
|
||||
}
|
||||
|
||||
if (OBJECT_HEADER_TO_NAME_INFO(Header))
|
||||
{
|
||||
if(OBJECT_HEADER_TO_NAME_INFO(Header)->Name.Buffer)
|
||||
{
|
||||
ExFreePool(OBJECT_HEADER_TO_NAME_INFO(Header)->Name.Buffer);
|
||||
}
|
||||
}
|
||||
if (Header->ObjectCreateInfo)
|
||||
{
|
||||
ObpReleaseCapturedAttributes(Header->ObjectCreateInfo);
|
||||
ExFreePool(Header->ObjectCreateInfo);
|
||||
}
|
||||
/* Get the header and assume this is what we'll free */
|
||||
Header = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
ObjectType = Header->Type;
|
||||
HeaderLocation = Header;
|
||||
|
||||
/* To find the header, walk backwards from how we allocated */
|
||||
if ((CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(Header)))
|
||||
{
|
||||
HeaderLocation = CreatorInfo;
|
||||
}
|
||||
}
|
||||
if ((NameInfo = OBJECT_HEADER_TO_NAME_INFO(Header)))
|
||||
{
|
||||
HeaderLocation = NameInfo;
|
||||
|
@ -83,88 +57,124 @@ ObpDeleteObject(POBJECT_HEADER Header)
|
|||
HeaderLocation = HandleInfo;
|
||||
}
|
||||
|
||||
DPRINT("ObPerformRetentionChecks() = Freeing object\n");
|
||||
ExFreePool(HeaderLocation);
|
||||
|
||||
return(STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
VOID STDCALL
|
||||
ObpDeleteObjectWorkRoutine (IN PVOID Parameter)
|
||||
{
|
||||
PRETENTION_CHECK_PARAMS Params = (PRETENTION_CHECK_PARAMS)Parameter;
|
||||
/* ULONG Tag; */ /* See below */
|
||||
|
||||
ASSERT(Params);
|
||||
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); /* We need PAGED_CODE somewhere... */
|
||||
|
||||
/* Turn this on when we have ExFreePoolWithTag
|
||||
Tag = Params->ObjectHeader->Type->Tag; */
|
||||
ObpDeleteObject(Params->ObjectHeader);
|
||||
ExFreePool(Params);
|
||||
/* ExFreePoolWithTag(Params, Tag); */
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS
|
||||
ObpDeleteObjectDpcLevel(IN POBJECT_HEADER ObjectHeader,
|
||||
IN LONG OldPointerCount)
|
||||
{
|
||||
#if 0
|
||||
if (ObjectHeader->PointerCount < 0)
|
||||
/* Check if we have create info */
|
||||
if (Header->Flags & OB_FLAG_CREATE_INFO)
|
||||
{
|
||||
CPRINT("Object %p/%p has invalid reference count (%d)\n",
|
||||
ObjectHeader, HEADER_TO_BODY(ObjectHeader),
|
||||
ObjectHeader->PointerCount);
|
||||
KEBUGCHECK(0);
|
||||
}
|
||||
|
||||
if (ObjectHeader->HandleCount < 0)
|
||||
{
|
||||
CPRINT("Object %p/%p has invalid handle count (%d)\n",
|
||||
ObjectHeader, HEADER_TO_BODY(ObjectHeader),
|
||||
ObjectHeader->HandleCount);
|
||||
KEBUGCHECK(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
switch (KeGetCurrentIrql ())
|
||||
{
|
||||
case PASSIVE_LEVEL:
|
||||
return ObpDeleteObject (ObjectHeader);
|
||||
|
||||
case APC_LEVEL:
|
||||
case DISPATCH_LEVEL:
|
||||
/* Double-check that it exists */
|
||||
if (Header->ObjectCreateInfo)
|
||||
{
|
||||
PRETENTION_CHECK_PARAMS Params;
|
||||
|
||||
/*
|
||||
We use must succeed pool here because if the allocation fails
|
||||
then we leak memory.
|
||||
*/
|
||||
Params = (PRETENTION_CHECK_PARAMS)
|
||||
ExAllocatePoolWithTag(NonPagedPoolMustSucceed,
|
||||
sizeof(RETENTION_CHECK_PARAMS),
|
||||
ObjectHeader->Type->Key);
|
||||
Params->ObjectHeader = ObjectHeader;
|
||||
ExInitializeWorkItem(&Params->WorkItem,
|
||||
ObpDeleteObjectWorkRoutine,
|
||||
(PVOID)Params);
|
||||
ExQueueWorkItem(&Params->WorkItem,
|
||||
CriticalWorkQueue);
|
||||
/* Free it */
|
||||
ObpReleaseCapturedAttributes(Header->ObjectCreateInfo);
|
||||
Header->ObjectCreateInfo = NULL;
|
||||
}
|
||||
return STATUS_PENDING;
|
||||
|
||||
default:
|
||||
DPRINT("ObpDeleteObjectDpcLevel called at unsupported "
|
||||
"IRQL %u!\n", KeGetCurrentIrql());
|
||||
KEBUGCHECK(0);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
/* Check if a handle database was active */
|
||||
if ((HandleInfo) && (Header->Flags & OB_FLAG_SINGLE_PROCESS))
|
||||
{
|
||||
/* Free it */
|
||||
ExFreePool(HandleInfo->HandleCountDatabase);
|
||||
HandleInfo->HandleCountDatabase = NULL;
|
||||
}
|
||||
|
||||
/* Check if we have a name */
|
||||
if ((NameInfo) && (NameInfo->Name.Buffer))
|
||||
{
|
||||
/* Free it */
|
||||
ExFreePool(NameInfo->Name.Buffer);
|
||||
NameInfo->Name.Buffer = NULL;
|
||||
}
|
||||
|
||||
/* Free the object using the same allocation tag */
|
||||
ExFreePoolWithTag(HeaderLocation,
|
||||
ObjectType ? TAG('T', 'j', 'b', 'O') : ObjectType->Key);
|
||||
|
||||
/* Decrease the total */
|
||||
ObjectType->TotalNumberOfObjects--;
|
||||
}
|
||||
|
||||
VOID
|
||||
FASTCALL
|
||||
ObpDeleteObject(IN PVOID Object)
|
||||
{
|
||||
POBJECT_HEADER Header;
|
||||
POBJECT_TYPE ObjectType;
|
||||
POBJECT_HEADER_NAME_INFO NameInfo;
|
||||
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Get the header and type */
|
||||
Header = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
ObjectType = Header->Type;
|
||||
|
||||
/* Get creator and name information */
|
||||
NameInfo = OBJECT_HEADER_TO_NAME_INFO(Header);
|
||||
CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(Header);
|
||||
|
||||
/* Check if the object is on a type list */
|
||||
if ((CreatorInfo) && !(IsListEmpty(&CreatorInfo->TypeList)))
|
||||
{
|
||||
/* Remove the object from the type list */
|
||||
RemoveEntryList(&CreatorInfo->TypeList);
|
||||
}
|
||||
|
||||
/* Check if we have a name */
|
||||
if ((NameInfo) && (NameInfo->Name.Buffer))
|
||||
{
|
||||
/* Free it */
|
||||
ExFreePool(NameInfo->Name.Buffer);
|
||||
|
||||
/* Clean up the string so we don't try this again */
|
||||
RtlInitUnicodeString(&NameInfo->Name, NULL);
|
||||
}
|
||||
|
||||
/* Check if we have a security descriptor */
|
||||
if (Header->SecurityDescriptor)
|
||||
{
|
||||
ObjectType->TypeInfo.SecurityProcedure(Object,
|
||||
DeleteSecurityDescriptor,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
&Header->SecurityDescriptor,
|
||||
0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Check if we have a delete procedure */
|
||||
if (ObjectType->TypeInfo.DeleteProcedure)
|
||||
{
|
||||
/* Call it */
|
||||
ObjectType->TypeInfo.DeleteProcedure(Object);
|
||||
}
|
||||
|
||||
/* Now de-allocate all object members */
|
||||
ObpDeallocateObject(Object);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
ObpReapObject(IN PVOID Parameter)
|
||||
{
|
||||
POBJECT_HEADER ReapObject;
|
||||
PVOID NextObject;
|
||||
|
||||
/* Start reaping */
|
||||
while((ReapObject = InterlockedExchangePointer(&ObpReaperList, NULL)))
|
||||
{
|
||||
/* Start deletion loop */
|
||||
do
|
||||
{
|
||||
/* Get the next object */
|
||||
NextObject = ReapObject->NextToFree;
|
||||
|
||||
/* Delete the object */
|
||||
ObpDeleteObject(&ReapObject->Body);
|
||||
|
||||
/* Move to the next one */
|
||||
ReapObject = NextObject;
|
||||
} while(NextObject != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
|
|
|
@ -104,28 +104,37 @@ FASTCALL
|
|||
ObfDereferenceObject(IN PVOID Object)
|
||||
{
|
||||
POBJECT_HEADER Header;
|
||||
LONG NewPointerCount;
|
||||
BOOL Permanent;
|
||||
|
||||
ASSERT(Object);
|
||||
|
||||
/* Extract the object header. */
|
||||
/* Extract the object header */
|
||||
Header = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
Permanent = Header->Flags & OB_FLAG_PERMANENT;
|
||||
|
||||
/*
|
||||
Drop our reference and get the new count so we can tell if this was the
|
||||
last reference.
|
||||
*/
|
||||
NewPointerCount = InterlockedDecrement(&Header->PointerCount);
|
||||
DPRINT("ObfDereferenceObject(0x%x)==%d\n", Object, NewPointerCount);
|
||||
ASSERT(NewPointerCount >= 0);
|
||||
|
||||
/* Check whether the object can now be deleted. */
|
||||
if (NewPointerCount == 0 &&
|
||||
!Permanent)
|
||||
if (!(InterlockedDecrement(&Header->PointerCount)) &&
|
||||
!(Header->Flags & OB_FLAG_PERMANENT))
|
||||
{
|
||||
ObpDeleteObjectDpcLevel(Header, NewPointerCount);
|
||||
/* Sanity check */
|
||||
ASSERT(!Header->HandleCount);
|
||||
|
||||
/* Check if we're at PASSIVE */
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL)
|
||||
{
|
||||
/* Remove the object */
|
||||
ObpDeleteObject(Object);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add us to the list */
|
||||
do
|
||||
{
|
||||
Header->NextToFree = ObpReaperList;
|
||||
} while (InterlockedCompareExchangePointer(&ObpReaperList,
|
||||
Header,
|
||||
Header->NextToFree) !=
|
||||
Header->NextToFree);
|
||||
|
||||
/* Queue the work item */
|
||||
ExQueueWorkItem(&ObpReaperWorkItem, DelayedWorkQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue