1182 lines
27 KiB
C
1182 lines
27 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1989-1995 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
callback.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements the executive callbaqck object. Functions are
|
||
|
provided to open, register, unregister , and notify callback objects.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Ken Reneris (kenr) 7-March-1995
|
||
|
|
||
|
Neill Clift (NeillC) 17-Feb-2001
|
||
|
|
||
|
Added low overhead callbacks for critical components like thread/registry etc.
|
||
|
These routines have a high probability of not requiring any locks for an
|
||
|
individual call.
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode only.
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#include "exp.h"
|
||
|
|
||
|
//
|
||
|
// Callback Specific Access Rights.
|
||
|
//
|
||
|
|
||
|
#define CALLBACK_MODIFY_STATE 0x0001
|
||
|
|
||
|
#define CALLBACK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|\
|
||
|
CALLBACK_MODIFY_STATE )
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Event to wait for registration to become idle
|
||
|
//
|
||
|
|
||
|
KEVENT ExpCallbackEvent;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Lock used when fast referencing fails.
|
||
|
//
|
||
|
EX_PUSH_LOCK ExpCallBackFlush;
|
||
|
|
||
|
//
|
||
|
// Debug flag to force certain code paths. Let it get optimized away on free builds.
|
||
|
//
|
||
|
#if DBG
|
||
|
|
||
|
BOOLEAN ExpCallBackReturnRefs = FALSE;
|
||
|
|
||
|
#else
|
||
|
|
||
|
const
|
||
|
BOOLEAN ExpCallBackReturnRefs = FALSE;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Address of callback object type descriptor.
|
||
|
//
|
||
|
|
||
|
POBJECT_TYPE ExCallbackObjectType;
|
||
|
|
||
|
//
|
||
|
// Structure that describes the mapping of generic access rights to object
|
||
|
// specific access rights for callback objects.
|
||
|
//
|
||
|
|
||
|
#ifdef ALLOC_DATA_PRAGMA
|
||
|
#pragma const_seg("INITCONST")
|
||
|
#endif
|
||
|
const GENERIC_MAPPING ExpCallbackMapping = {
|
||
|
STANDARD_RIGHTS_READ ,
|
||
|
STANDARD_RIGHTS_WRITE | CALLBACK_MODIFY_STATE,
|
||
|
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
|
||
|
CALLBACK_ALL_ACCESS
|
||
|
};
|
||
|
#ifdef ALLOC_DATA_PRAGMA
|
||
|
#pragma const_seg()
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Executive callback object structure definition.
|
||
|
//
|
||
|
|
||
|
typedef struct _CALLBACK_OBJECT {
|
||
|
ULONG Signature;
|
||
|
KSPIN_LOCK Lock;
|
||
|
LIST_ENTRY RegisteredCallbacks;
|
||
|
BOOLEAN AllowMultipleCallbacks;
|
||
|
UCHAR reserved[3];
|
||
|
} CALLBACK_OBJECT , *PCALLBACK_OBJECT;
|
||
|
|
||
|
//
|
||
|
// Executive callback registration structure definition.
|
||
|
//
|
||
|
|
||
|
typedef struct _CALLBACK_REGISTRATION {
|
||
|
LIST_ENTRY Link;
|
||
|
PCALLBACK_OBJECT CallbackObject;
|
||
|
PCALLBACK_FUNCTION CallbackFunction;
|
||
|
PVOID CallbackContext;
|
||
|
ULONG Busy;
|
||
|
BOOLEAN UnregisterWaiting;
|
||
|
} CALLBACK_REGISTRATION , *PCALLBACK_REGISTRATION;
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ExpDeleteCallback (
|
||
|
IN PCALLBACK_OBJECT CallbackObject
|
||
|
);
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(INIT, ExpInitializeCallbacks)
|
||
|
#pragma alloc_text(PAGE, ExCreateCallback)
|
||
|
#pragma alloc_text(PAGE, ExpDeleteCallback)
|
||
|
#pragma alloc_text(PAGE, ExInitializeCallBack)
|
||
|
#pragma alloc_text(PAGE, ExCompareExchangeCallBack)
|
||
|
#pragma alloc_text(PAGE, ExCallCallBack)
|
||
|
#pragma alloc_text(PAGE, ExFreeCallBack)
|
||
|
#pragma alloc_text(PAGE, ExAllocateCallBack)
|
||
|
#pragma alloc_text(PAGE, ExReferenceCallBackBlock)
|
||
|
#pragma alloc_text(PAGE, ExGetCallBackBlockRoutine)
|
||
|
#pragma alloc_text(PAGE, ExWaitForCallBacks)
|
||
|
#pragma alloc_text(PAGE, ExGetCallBackBlockContext)
|
||
|
#pragma alloc_text(PAGE, ExDereferenceCallBackBlock)
|
||
|
#endif
|
||
|
|
||
|
BOOLEAN
|
||
|
ExpInitializeCallbacks (
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function creates the callback object type descriptor at system
|
||
|
initialization and stores the address of the object type descriptor
|
||
|
in local static storage.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
A value of TRUE is returned if the timer object type descriptor is
|
||
|
successfully initialized. Otherwise a value of FALSE is returned.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
NTSTATUS Status;
|
||
|
UNICODE_STRING unicodeString;
|
||
|
ULONG i;
|
||
|
HANDLE handle;
|
||
|
|
||
|
//
|
||
|
// Initialize the slow referencing lock
|
||
|
//
|
||
|
ExInitializePushLock (&ExpCallBackFlush);
|
||
|
|
||
|
//
|
||
|
// Initialize string descriptor.
|
||
|
//
|
||
|
|
||
|
RtlInitUnicodeString(&unicodeString, L"Callback");
|
||
|
|
||
|
//
|
||
|
// Create timer object type descriptor.
|
||
|
//
|
||
|
|
||
|
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
|
||
|
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
|
||
|
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
|
||
|
ObjectTypeInitializer.GenericMapping = ExpCallbackMapping;
|
||
|
ObjectTypeInitializer.DeleteProcedure = ExpDeleteCallback;
|
||
|
ObjectTypeInitializer.PoolType = NonPagedPool;
|
||
|
ObjectTypeInitializer.ValidAccessMask = CALLBACK_ALL_ACCESS;
|
||
|
Status = ObCreateObjectType(&unicodeString,
|
||
|
&ObjectTypeInitializer,
|
||
|
(PSECURITY_DESCRIPTOR)NULL,
|
||
|
&ExCallbackObjectType);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RtlInitUnicodeString( &unicodeString, ExpWstrCallback );
|
||
|
InitializeObjectAttributes(
|
||
|
&ObjectAttributes,
|
||
|
&unicodeString,
|
||
|
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
|
||
|
NULL,
|
||
|
SePublicDefaultSd
|
||
|
);
|
||
|
|
||
|
Status = NtCreateDirectoryObject(
|
||
|
&handle,
|
||
|
DIRECTORY_ALL_ACCESS,
|
||
|
&ObjectAttributes
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
NtClose (handle);
|
||
|
|
||
|
//
|
||
|
// Initialize event to wait on for Unregisters which occur while
|
||
|
// notifications are in progress
|
||
|
//
|
||
|
|
||
|
KeInitializeEvent (&ExpCallbackEvent, NotificationEvent, 0);
|
||
|
|
||
|
//
|
||
|
// Initialize NT global callbacks
|
||
|
//
|
||
|
|
||
|
for (i=0; ExpInitializeCallback[i].CallBackObject; i++) {
|
||
|
|
||
|
//
|
||
|
// Create named calledback
|
||
|
//
|
||
|
|
||
|
RtlInitUnicodeString(&unicodeString, ExpInitializeCallback[i].CallbackName);
|
||
|
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&ObjectAttributes,
|
||
|
&unicodeString,
|
||
|
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
Status = ExCreateCallback (
|
||
|
ExpInitializeCallback[i].CallBackObject,
|
||
|
&ObjectAttributes,
|
||
|
TRUE,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
ExCreateCallback (
|
||
|
OUT PCALLBACK_OBJECT * CallbackObject,
|
||
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
|
IN BOOLEAN Create,
|
||
|
IN BOOLEAN AllowMultipleCallbacks
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function opens a callback object with the specified callback
|
||
|
object. If the callback object does not exist or it is a NULL then
|
||
|
a callback object will be created if create is TRUE. If a callbackobject
|
||
|
is created it will only support multiple registered callbacks if
|
||
|
AllowMulitipleCallbacks is TRUE.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallbackObject - Supplies a pointer to a variable that will receive the
|
||
|
Callback object.
|
||
|
|
||
|
CallbackName - Supplies a pointer to a object name that will receive the
|
||
|
|
||
|
Create - Supplies a flag which indicates whether a callback object will
|
||
|
be created or not .
|
||
|
|
||
|
AllowMultipleCallbacks - Supplies a flag which indicates only support
|
||
|
mulitiple registered callbacks.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PCALLBACK_OBJECT cbObject;
|
||
|
NTSTATUS Status;
|
||
|
HANDLE Handle;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
//
|
||
|
// Initializing cbObject & Handle is not needed for correctness but without
|
||
|
// it the compiler cannot compile this code W4 to check for use of
|
||
|
// uninitialized variables.
|
||
|
//
|
||
|
|
||
|
Handle = NULL;
|
||
|
cbObject = NULL;
|
||
|
|
||
|
//
|
||
|
// If named callback, open handle to it
|
||
|
//
|
||
|
|
||
|
if (ObjectAttributes->ObjectName) {
|
||
|
Status = ObOpenObjectByName(ObjectAttributes,
|
||
|
ExCallbackObjectType,
|
||
|
KernelMode,
|
||
|
NULL,
|
||
|
0, // DesiredAccess,
|
||
|
NULL,
|
||
|
&Handle);
|
||
|
} else {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If not opened, check if callback should be created
|
||
|
//
|
||
|
|
||
|
if (!NT_SUCCESS(Status) && Create ) {
|
||
|
|
||
|
Status = ObCreateObject(KernelMode,
|
||
|
ExCallbackObjectType,
|
||
|
ObjectAttributes,
|
||
|
KernelMode,
|
||
|
NULL,
|
||
|
sizeof(CALLBACK_OBJECT),
|
||
|
0,
|
||
|
0,
|
||
|
(PVOID *)&cbObject );
|
||
|
|
||
|
if(NT_SUCCESS(Status)){
|
||
|
|
||
|
//
|
||
|
// Fill in structure signature
|
||
|
//
|
||
|
|
||
|
cbObject->Signature = 'llaC';
|
||
|
|
||
|
//
|
||
|
// It will support multiple registered callbacks if
|
||
|
// AllowMultipleCallbacks is TRUE.
|
||
|
//
|
||
|
|
||
|
cbObject->AllowMultipleCallbacks = AllowMultipleCallbacks;
|
||
|
|
||
|
//
|
||
|
// Initialize CallbackObject queue.
|
||
|
//
|
||
|
|
||
|
InitializeListHead( &cbObject->RegisteredCallbacks );
|
||
|
|
||
|
//
|
||
|
// Initialize spinlock
|
||
|
//
|
||
|
|
||
|
KeInitializeSpinLock (&cbObject->Lock);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Put the object in the root directory
|
||
|
//
|
||
|
|
||
|
Status = ObInsertObject (
|
||
|
cbObject,
|
||
|
NULL,
|
||
|
FILE_READ_DATA,
|
||
|
0,
|
||
|
NULL,
|
||
|
&Handle );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if(NT_SUCCESS(Status)){
|
||
|
|
||
|
//
|
||
|
// Add one to callback object reference count.
|
||
|
//
|
||
|
|
||
|
Status = ObReferenceObjectByHandle (
|
||
|
Handle,
|
||
|
0, // DesiredAccess
|
||
|
ExCallbackObjectType,
|
||
|
KernelMode,
|
||
|
&cbObject,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
ZwClose (Handle);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If success, returns a referenced pointer to the CallbackObject.
|
||
|
//
|
||
|
|
||
|
if (NT_SUCCESS(Status)) {
|
||
|
*CallbackObject = cbObject;
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ExpDeleteCallback (
|
||
|
IN PCALLBACK_OBJECT CallbackObject
|
||
|
)
|
||
|
{
|
||
|
#if !DBG
|
||
|
UNREFERENCED_PARAMETER (CallbackObject);
|
||
|
#endif
|
||
|
|
||
|
ASSERT (IsListEmpty(&CallbackObject->RegisteredCallbacks));
|
||
|
}
|
||
|
|
||
|
PVOID
|
||
|
ExRegisterCallback (
|
||
|
IN PCALLBACK_OBJECT CallbackObject,
|
||
|
IN PCALLBACK_FUNCTION CallbackFunction,
|
||
|
IN PVOID CallbackContext
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine allows a caller to register that it would like to have its
|
||
|
callback Function invoked when the callback notification call occurs.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallbackObject - Supplies a pointer to a CallbackObject.
|
||
|
|
||
|
CallbackFunction - Supplies a pointer to a function which is to
|
||
|
be executed when the Callback notification occures.
|
||
|
|
||
|
CallbackContext - Supplies a pointer to an arbitrary data structure
|
||
|
that will be passed to the function specified by the CallbackFunction
|
||
|
parameter.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns handle to callback registration.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PCALLBACK_REGISTRATION CallbackRegistration;
|
||
|
BOOLEAN Inserted;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
ASSERT (CallbackFunction);
|
||
|
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
|
||
|
|
||
|
//
|
||
|
// Add reference to object
|
||
|
//
|
||
|
|
||
|
ObReferenceObject (CallbackObject);
|
||
|
|
||
|
//
|
||
|
// Begin by attempting to allocate storage for the CallbackRegistration.
|
||
|
// one cannot be allocated, return the error status.
|
||
|
//
|
||
|
|
||
|
CallbackRegistration = ExAllocatePoolWithTag(
|
||
|
NonPagedPool,
|
||
|
sizeof( CALLBACK_REGISTRATION ),
|
||
|
'eRBC'
|
||
|
);
|
||
|
|
||
|
|
||
|
if( !CallbackRegistration ) {
|
||
|
ObDereferenceObject (CallbackObject);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize the callback packet
|
||
|
//
|
||
|
|
||
|
CallbackRegistration->CallbackObject = CallbackObject;
|
||
|
CallbackRegistration->CallbackFunction = CallbackFunction;
|
||
|
CallbackRegistration->CallbackContext = CallbackContext;
|
||
|
CallbackRegistration->Busy = 0;
|
||
|
CallbackRegistration->UnregisterWaiting = FALSE;
|
||
|
|
||
|
|
||
|
Inserted = FALSE;
|
||
|
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
|
||
|
|
||
|
if( CallbackObject->AllowMultipleCallbacks ||
|
||
|
IsListEmpty( &CallbackObject->RegisteredCallbacks ) ) {
|
||
|
|
||
|
//
|
||
|
// add CallbackRegistration to tail
|
||
|
//
|
||
|
|
||
|
|
||
|
Inserted = TRUE;
|
||
|
InsertTailList( &CallbackObject->RegisteredCallbacks,
|
||
|
&CallbackRegistration->Link );
|
||
|
}
|
||
|
|
||
|
KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
|
||
|
|
||
|
if (!Inserted) {
|
||
|
ExFreePool (CallbackRegistration);
|
||
|
CallbackRegistration = NULL;
|
||
|
}
|
||
|
|
||
|
return (PVOID) CallbackRegistration;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ExUnregisterCallback (
|
||
|
IN PVOID CbRegistration
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function removes the callback registration for the callbacks
|
||
|
from the list of callback object .
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallbackRegistration - Pointer to device object for the file system.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PCALLBACK_REGISTRATION CallbackRegistration;
|
||
|
PCALLBACK_OBJECT CallbackObject;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
|
||
|
|
||
|
CallbackRegistration = (PCALLBACK_REGISTRATION) CbRegistration;
|
||
|
CallbackObject = CallbackRegistration->CallbackObject;
|
||
|
|
||
|
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
|
||
|
|
||
|
//
|
||
|
// Wait for registration
|
||
|
//
|
||
|
|
||
|
while (CallbackRegistration->Busy) {
|
||
|
|
||
|
//
|
||
|
// Set waiting flag, then wait. (not performance critical - use
|
||
|
// single global event to wait for any and all unregister waits)
|
||
|
//
|
||
|
|
||
|
CallbackRegistration->UnregisterWaiting = TRUE;
|
||
|
KeClearEvent (&ExpCallbackEvent);
|
||
|
KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
|
||
|
|
||
|
KeWaitForSingleObject (
|
||
|
&ExpCallbackEvent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Synchronize with callback object and recheck registration busy
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Registration not busy, remove it from the callback object
|
||
|
//
|
||
|
|
||
|
RemoveEntryList (&CallbackRegistration->Link);
|
||
|
KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
|
||
|
|
||
|
//
|
||
|
// Free memory used for CallbackRegistration
|
||
|
//
|
||
|
|
||
|
ExFreePool (CallbackRegistration);
|
||
|
|
||
|
//
|
||
|
// Remove reference count on CallbackObject
|
||
|
//
|
||
|
|
||
|
ObDereferenceObject (CallbackObject);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ExNotifyCallback (
|
||
|
IN PCALLBACK_OBJECT CallbackObject,
|
||
|
IN PVOID Argument1,
|
||
|
IN PVOID Argument2
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function notifies all registered callbacks .
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallbackObject - supplies a pointer to the callback object should be
|
||
|
notified.
|
||
|
|
||
|
SystemArgument1 - supplies a pointer will be passed to callback function.
|
||
|
|
||
|
SystemArgument2 - supplies a pointer will be passed to callback function.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PLIST_ENTRY Link;
|
||
|
PCALLBACK_REGISTRATION CallbackRegistration;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
if (CallbackObject == NULL) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Synchronize with callback object
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
|
||
|
|
||
|
//
|
||
|
// call registered callbacks at callers IRQL level
|
||
|
// ( done if FIFO order of registration )
|
||
|
//
|
||
|
|
||
|
if (OldIrql == DISPATCH_LEVEL) {
|
||
|
|
||
|
//
|
||
|
// OldIrql is DISPATCH_LEVEL, just invoke all callbacks without
|
||
|
// releasing the lock
|
||
|
//
|
||
|
|
||
|
for (Link = CallbackObject->RegisteredCallbacks.Flink;
|
||
|
Link != &CallbackObject->RegisteredCallbacks;
|
||
|
Link = Link->Flink) {
|
||
|
|
||
|
//
|
||
|
// Get current registration to notify
|
||
|
//
|
||
|
|
||
|
CallbackRegistration = CONTAINING_RECORD (Link,
|
||
|
CALLBACK_REGISTRATION,
|
||
|
Link);
|
||
|
|
||
|
//
|
||
|
// Notify reigstration
|
||
|
//
|
||
|
|
||
|
CallbackRegistration->CallbackFunction(
|
||
|
CallbackRegistration->CallbackContext,
|
||
|
Argument1,
|
||
|
Argument2
|
||
|
);
|
||
|
|
||
|
} // next registration
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// OldIrql is < DISPATCH_LEVEL, the code being called may be pagable
|
||
|
// and the callback object spinlock needs to be released around
|
||
|
// each registration callback.
|
||
|
//
|
||
|
|
||
|
for (Link = CallbackObject->RegisteredCallbacks.Flink;
|
||
|
Link != &CallbackObject->RegisteredCallbacks;
|
||
|
Link = Link->Flink ) {
|
||
|
|
||
|
//
|
||
|
// Get current registration to notify
|
||
|
//
|
||
|
|
||
|
CallbackRegistration = CONTAINING_RECORD (Link,
|
||
|
CALLBACK_REGISTRATION,
|
||
|
Link);
|
||
|
|
||
|
//
|
||
|
// If registration is being removed, don't bothing calling it
|
||
|
//
|
||
|
|
||
|
if (!CallbackRegistration->UnregisterWaiting) {
|
||
|
|
||
|
//
|
||
|
// Set registration busy
|
||
|
//
|
||
|
|
||
|
CallbackRegistration->Busy += 1;
|
||
|
|
||
|
//
|
||
|
// Release SpinLock and notify this callback
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
|
||
|
|
||
|
CallbackRegistration->CallbackFunction(
|
||
|
CallbackRegistration->CallbackContext,
|
||
|
Argument1,
|
||
|
Argument2
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Synchronize with CallbackObject
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
|
||
|
|
||
|
//
|
||
|
// Remove our busy count
|
||
|
//
|
||
|
|
||
|
CallbackRegistration->Busy -= 1;
|
||
|
|
||
|
//
|
||
|
// If the registriation removal is pending, kick global
|
||
|
// event let unregister conitnue
|
||
|
//
|
||
|
|
||
|
if (CallbackRegistration->UnregisterWaiting &&
|
||
|
CallbackRegistration->Busy == 0) {
|
||
|
KeSetEvent (&ExpCallbackEvent, 0, FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Release callback
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ExInitializeCallBack (
|
||
|
IN OUT PEX_CALLBACK CallBack
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function initializes a low overhead callback.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBack - Pointer to the callback structure
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ExFastRefInitialize (&CallBack->RoutineBlock, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK
|
||
|
ExAllocateCallBack (
|
||
|
IN PEX_CALLBACK_FUNCTION Function,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function allocates a low overhead callback.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Function - Routine to issue callbacks to
|
||
|
Context - A context value to issue
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK - Allocated block or NULL if allocation fails.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK NewBlock;
|
||
|
|
||
|
NewBlock = ExAllocatePoolWithTag (PagedPool,
|
||
|
sizeof (EX_CALLBACK_ROUTINE_BLOCK),
|
||
|
'brbC');
|
||
|
if (NewBlock != NULL) {
|
||
|
NewBlock->Function = Function;
|
||
|
NewBlock->Context = Context;
|
||
|
ExInitializeRundownProtection (&NewBlock->RundownProtect);
|
||
|
}
|
||
|
return NewBlock;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ExFreeCallBack (
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function destroys a low overhead callback block.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBackBlock - Call back block to destroy
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
ExFreePool (CallBackBlock);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ExWaitForCallBacks (
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function waits for all outcalls on the specified
|
||
|
callback block to complete
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBackBlock - Call back block to wait for
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Wait for all active callbacks to be finished.
|
||
|
//
|
||
|
ExWaitForRundownProtectionRelease (&CallBackBlock->RundownProtect);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
ExCompareExchangeCallBack (
|
||
|
IN OUT PEX_CALLBACK CallBack,
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock,
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function assigns, removes or swaps a low overhead callback function.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBack - Callback structure to be modified
|
||
|
|
||
|
NewBlock - New block to be installed in the callback
|
||
|
|
||
|
OldBlock - The old block that must be there now to be replaced
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOLEAN - TRUE: The swap occured, FALSE: The swap failed
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
EX_FAST_REF OldRef;
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK ReplacedBlock;
|
||
|
|
||
|
if (NewBlock != NULL) {
|
||
|
//
|
||
|
// Add the additional references to the routine block
|
||
|
//
|
||
|
if (!ExAcquireRundownProtectionEx (&NewBlock->RundownProtect,
|
||
|
ExFastRefGetAdditionalReferenceCount () + 1)) {
|
||
|
ASSERTMSG ("Callback block is already undergoing rundown", FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Attempt to replace the existing object and balance all the reference counts
|
||
|
//
|
||
|
OldRef = ExFastRefCompareSwapObject (&CallBack->RoutineBlock,
|
||
|
NewBlock,
|
||
|
OldBlock);
|
||
|
|
||
|
ReplacedBlock = ExFastRefGetObject (OldRef);
|
||
|
|
||
|
//
|
||
|
// See if the swap occured. If it didn't undo the original references we added.
|
||
|
// If it did then release remaining references on the original
|
||
|
//
|
||
|
if (ReplacedBlock == OldBlock) {
|
||
|
PKTHREAD CurrentThread;
|
||
|
//
|
||
|
// We need to flush out any slow referencers at this point. We do this by
|
||
|
// acquiring and releasing a lock.
|
||
|
//
|
||
|
if (ReplacedBlock != NULL) {
|
||
|
CurrentThread = KeGetCurrentThread ();
|
||
|
|
||
|
KeEnterCriticalRegionThread (CurrentThread);
|
||
|
|
||
|
ExAcquireReleasePushLockExclusive (&ExpCallBackFlush);
|
||
|
|
||
|
KeLeaveCriticalRegionThread (CurrentThread);
|
||
|
|
||
|
ExReleaseRundownProtectionEx (&ReplacedBlock->RundownProtect,
|
||
|
ExFastRefGetUnusedReferences (OldRef) + 1);
|
||
|
|
||
|
}
|
||
|
return TRUE;
|
||
|
} else {
|
||
|
//
|
||
|
// The swap failed. Remove the addition references if we had added any.
|
||
|
//
|
||
|
if (NewBlock != NULL) {
|
||
|
ExReleaseRundownProtectionEx (&NewBlock->RundownProtect,
|
||
|
ExFastRefGetAdditionalReferenceCount () + 1);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK
|
||
|
ExReferenceCallBackBlock (
|
||
|
IN OUT PEX_CALLBACK CallBack
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function takes a reference on the call back block inside the
|
||
|
callback structure.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBack - Call back to obtain the call back block from
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK - Referenced structure or NULL if these wasn't one
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
EX_FAST_REF OldRef;
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock;
|
||
|
|
||
|
//
|
||
|
// Get a reference to the callback block if we can.
|
||
|
//
|
||
|
OldRef = ExFastReference (&CallBack->RoutineBlock);
|
||
|
|
||
|
//
|
||
|
// If there is no callback then return
|
||
|
//
|
||
|
if (ExFastRefObjectNull (OldRef)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
//
|
||
|
// If we didn't get a reference then use a lock to get one.
|
||
|
//
|
||
|
if (!ExFastRefCanBeReferenced (OldRef)) {
|
||
|
PKTHREAD CurrentThread;
|
||
|
CurrentThread = KeGetCurrentThread ();
|
||
|
|
||
|
KeEnterCriticalRegionThread (CurrentThread);
|
||
|
|
||
|
ExAcquirePushLockExclusive (&ExpCallBackFlush);
|
||
|
|
||
|
CallBackBlock = ExFastRefGetObject (CallBack->RoutineBlock);
|
||
|
if (CallBackBlock && !ExAcquireRundownProtection (&CallBackBlock->RundownProtect)) {
|
||
|
CallBackBlock = NULL;
|
||
|
}
|
||
|
|
||
|
ExReleasePushLockExclusive (&ExpCallBackFlush);
|
||
|
|
||
|
KeLeaveCriticalRegionThread (CurrentThread);
|
||
|
|
||
|
if (CallBackBlock == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
CallBackBlock = ExFastRefGetObject (OldRef);
|
||
|
|
||
|
//
|
||
|
// If we just removed the last reference then attempt fix it up.
|
||
|
//
|
||
|
if (ExFastRefIsLastReference (OldRef) && !ExpCallBackReturnRefs) {
|
||
|
ULONG RefsToAdd;
|
||
|
|
||
|
RefsToAdd = ExFastRefGetAdditionalReferenceCount ();
|
||
|
|
||
|
//
|
||
|
// If we can't add the references then just give up
|
||
|
//
|
||
|
if (ExAcquireRundownProtectionEx (&CallBackBlock->RundownProtect,
|
||
|
RefsToAdd)) {
|
||
|
//
|
||
|
// Repopulate the cached refs. If this fails we just give them back.
|
||
|
//
|
||
|
if (!ExFastRefAddAdditionalReferenceCounts (&CallBack->RoutineBlock,
|
||
|
CallBackBlock,
|
||
|
RefsToAdd)) {
|
||
|
ExReleaseRundownProtectionEx (&CallBackBlock->RundownProtect,
|
||
|
RefsToAdd);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return CallBackBlock;
|
||
|
}
|
||
|
|
||
|
PEX_CALLBACK_FUNCTION
|
||
|
ExGetCallBackBlockRoutine (
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function gets the routine associated with a call back block
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBackBlock - Call back block to obtain routine for
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
PEX_CALLBACK_FUNCTION - The function pointer associated with this block
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
return CallBackBlock->Function;
|
||
|
}
|
||
|
|
||
|
PVOID
|
||
|
ExGetCallBackBlockContext (
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function gets the context associated with a call back block
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBackBlock - Call back block to obtain context for
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
PVOID - The context associated with this block
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
return CallBackBlock->Context;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ExDereferenceCallBackBlock (
|
||
|
IN OUT PEX_CALLBACK CallBack,
|
||
|
IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This returns a reference previous obtained on a call back block
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBackBlock - Call back block to return reference to
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (ExpCallBackReturnRefs || !ExFastRefDereference (&CallBack->RoutineBlock, CallBackBlock)) {
|
||
|
ExReleaseRundownProtection (&CallBackBlock->RundownProtect);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
ExCallCallBack (
|
||
|
IN OUT PEX_CALLBACK CallBack,
|
||
|
IN PVOID Argument1,
|
||
|
IN PVOID Argument2
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function calls the callback thats inside a callback structure
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CallBack - Call back that needs to be called through
|
||
|
|
||
|
Argument1 - Caller provided argument to pass on
|
||
|
|
||
|
Argument2 - Caller provided argument to pass on
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status returned by callback or STATUS_SUCCESS if theres wasn't one
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
CallBackBlock = ExReferenceCallBackBlock (CallBack);
|
||
|
if (CallBackBlock) {
|
||
|
//
|
||
|
// Call the function
|
||
|
//
|
||
|
Status = CallBackBlock->Function (CallBackBlock->Context, Argument1, Argument2);
|
||
|
|
||
|
ExDereferenceCallBackBlock (CallBack, CallBackBlock);
|
||
|
} else {
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
return Status;
|
||
|
}
|