windows-nt/Source/XPSP1/NT/base/cluster/clusnet/driver/event.c
2020-09-26 16:20:57 +08:00

1123 lines
30 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
event.c
Abstract:
general kernel to user event facility. Maintains a list of events that have
occurred and delivers them to Umode via the completion of an IOCTL IRP.
How this works:
The consumer opens a handle to clusnet and issues an
IOCTL_CLUSNET_SET_EVENT_MASK IRP indicating a mask of the events in which it
is interested. A kernel consumer must also supply a callback routine through
which it is notified about the event, i.e, they don't need to drop an
IOCTL_CLUSNET_GET_NEXT_EVENT IRP to receive notifications. The consumer is
linked onto the EventFileHandles list through the Linkage field in the
CN_FSCONTEXT structure. All synchronization is provided through one lock
called EventLock.
Umode consumers issue an IOCTL_CLUSNET_GET_NEXT_EVENT IRP to reap the next
interesting event. If no events are queued, the IRP is marked pending and a
pointer to it is stored in the FS context. CnEventIrpCancel is set as the
cancel routine. Note that only one IRP can be pending at a time; if an IRP
is already queued, this one is completed with STATUS_UNSUCCESSFUL.
If an event is waiting, it is removed from the FS context's list, the data
copied to the IRP's buffer and completed with success.
Posters call CnIssueEvent to post the event of interest. This obtains the
event lock, walks the file object list, and for consumers that are
interested in this event, allocates an Event context block (maintained as a
nonpaged lookaside list) and queues it to that file object's list of
events. It then posts a work queue item to run CnpDeliverEvents. We can't do
IRP processing directly since that would violate lock ordering within
clusnet.
CnDeliverEvents obtains the IO cancel and event locks, then runs the file
context list to see if there are events queued for any pending IRPs. If so,
the event data is copied to the systembuffer and the IRP is completed.
Author:
Charlie Wickham (charlwi) 17-Feb-1997
Environment:
Kernel Mode
Revision History:
Charlie Wickham (charlwi) 25-Oct-1999
Split CnIssueEvent into two routines: CnIssueEvent which strictly looks
up the apporpriate consumers of the event and CnpDeliverEvents which
runs at IRQL 0 to complete any IRPs that are waiting for new
events. This was done to prevent out of order event delivery; since the
event lock was near the top of locks to acquire first, the net IF down
events had to be queued to a worker thread which was bad. Now the event
lock is lowest which doesn't require a worker thread to post. The worker
thread still runs when it detects that there is an IRP waiting for an
event.
David Dion (daviddio) 29-Nov-2000
Disallow modification of the EventFileHandles list while event
deliveries are in process. Because CnpDeliverEvents and CnIssueEvent
drop their locks to deliver (via IRP completion and kmode callback,
respectively), a race condition can occur where an FS context event
mask is cleared and the FS context linkage fields are reset.
Modification of the EventFileHandles list is prevented using a count
of currently delivering threads that is protected by the EventLock.
--*/
#include "precomp.h"
#pragma hdrstop
#include "event.tmh"
/* Forward */
NTSTATUS
CnSetEventMask(
IN PCN_FSCONTEXT FsContext,
IN PCLUSNET_SET_EVENT_MASK_REQUEST EventRequest
);
NTSTATUS
CnGetNextEvent(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
CnIssueEvent(
CLUSNET_EVENT_TYPE Event,
CL_NODE_ID NodeId OPTIONAL,
CL_NETWORK_ID NetworkId OPTIONAL
);
VOID
CnEventIrpCancel(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
/* End Forward */
VOID
CnStartEventDelivery(
VOID
)
/*++
Routine Description:
Synchronizes iteration through EventFileHandles list with respect
to the EventDeliveryInProgress counter and the EventDeliveryComplete
KEVENT.
Arguments:
None.
Return value:
None.
Notes:
Called with and returns with EventLock held.
--*/
{
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
0, // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
CnAssert(EventDeliveryInProgress >= 0);
if (++EventDeliveryInProgress == 1) {
#if DBG
if (KeResetEvent(&EventDeliveryComplete) == 0) {
CnAssert(FALSE);
}
#else // DBG
KeClearEvent(&EventDeliveryComplete);
#endif // DBG
}
EventRevisitRequired = FALSE;
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
0, // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
} // CnStartEventDelivery
BOOLEAN
CnStopEventDelivery(
VOID
)
/**
Routine Description:
Synchronizes iteration through EventFileHandles list with respect
to the EventDeliveryInProgress counter and the EventDeliveryComplete
KEVENT. Checks the EventRevisitRequired flag to determine if an
event IRP arrived during the preceding delivery.
When signalling EventDeliveryComplete, IO_NETWORK_INCREMENT is used
to try to avoid starvation of waiters versus other event-delivering
threads.
Arguments:
None.
Return value:
TRUE if a new event or event IRP may have been added to the
EventFileHandles list, and it is necessary to rescan.
Notes:
Called with and returns with EventLock held.
--*/
{
BOOLEAN eventRevisitRequired = EventRevisitRequired;
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
0, // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
EventRevisitRequired = FALSE;
CnAssert(EventDeliveryInProgress >= 1);
if (--EventDeliveryInProgress == 0) {
if (KeSetEvent(
&EventDeliveryComplete,
IO_NETWORK_INCREMENT,
FALSE
) != 0) {
CnAssert(FALSE);
}
}
if (eventRevisitRequired) {
CnTrace(
EVENT_DETAIL, StopDeliveryRevisitRequired,
"[CN] CnStopEventDelivery: revisit required."
);
}
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
0, // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
return(eventRevisitRequired);
} // CnStopEventDelivery
BOOLEAN
CnIsEventDeliveryInProgress(
VOID
)
/*++
Routine Description:
Checks the EventDeliveryInProgress counter to determine if
an event delivery is in progress. If so, sets the
EventRevisitRequired flag.
Arguments:
None.
Return value:
TRUE if event delivery in progress.
Notes:
Called with and returns with EventLock held.
--*/
{
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
0, // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
if (EventDeliveryInProgress > 0) {
return(EventRevisitRequired = TRUE);
} else {
return(FALSE);
}
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
0, // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
} // CnIsEventDeliveryInProgress
BOOLEAN
CnWaitForEventDelivery(
IN PKIRQL EventLockIrql
)
/*++
Routine Description:
Waits for EventDeliveryComplete event to be signalled as long
as EventDeliveryInProgress counter is greater than zero.
Maintains a starvation counter to avoid looping forever.
Starvation threshold of 100 was chosen arbitrarily.
Arguments:
EventLockIrql - irql at which EventLock was acquired
Return value:
TRUE if returning with no deliveries in progress
FALSE if starvation threshold is exceeded and returning with
deliveries in progress
Notes:
Called with and returns with EventLock held; however, EventLock
may be dropped and reacquired during execution.
This call blocks, so no other spinlocks may be held at
invocation.
--*/
{
NTSTATUS status;
ULONG starvationCounter;
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
(ULONG) ~(CNP_EVENT_LOCK), // Forbidden
CNP_EVENT_LOCK_MAX // Maximum
);
starvationCounter = 100;
while (starvationCounter-- > 0) {
if (EventDeliveryInProgress == 0) {
return(TRUE);
}
CnReleaseLock(&EventLock, *EventLockIrql);
status = KeWaitForSingleObject(
&EventDeliveryComplete,
Executive,
KernelMode,
FALSE,
NULL
);
CnAssert(status == STATUS_SUCCESS);
CnAcquireLock(&EventLock, EventLockIrql);
}
CnTrace(
EVENT_DETAIL, EventWaitStarvation,
"[CN] CnWaitForEventDelivery: starvation threshold %u "
"exceeded.",
starvationCounter
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnWaitForEventDelivery: starvation threshold "
"expired.\n"));
}
CnVerifyCpuLockMask(
CNP_EVENT_LOCK, // Required
(ULONG) ~(CNP_EVENT_LOCK), // Forbidden
CNP_EVENT_LOCK // Maximum
);
return(FALSE);
} // CnWaitForEventDelivery
NTSTATUS
CnSetEventMask(
IN PCN_FSCONTEXT FsContext,
IN PCLUSNET_SET_EVENT_MASK_REQUEST EventRequest
)
/*++
Routine Description:
For a given file handle context, set the event mask associated
with it
Arguments:
FsContext - pointer to the clusnet file handle context block
EventMask - mask of interested events
Return Value:
STATUS_TIMEOUT if unable to modify EventFileHandles list.
STATUS_INVALID_PARAMETER_MIX if providing NULL event mask on
first call
STATUS_SUCCESS on success.
Notes:
This call may block.
--*/
{
CN_IRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY NextEntry;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
CnAcquireLock( &EventLock, &OldIrql );
#if 0
PCN_FSCONTEXT ListFsContext;
NextEntry = EventFileHandles.Flink;
while ( NextEntry != &EventFileHandles ) {
ListFsContext = CONTAINING_RECORD( NextEntry, CN_FSCONTEXT, Linkage );
if ( ListFsContext == FsContext ) {
break;
}
NextEntry = ListFsContext->Linkage.Flink;
}
#endif
if ( EventRequest->EventMask != 0 ) {
//
// adding or updating a handle. If not in the list then add them.
// Remember the events and, if appropriate, the callback func to use
// when an event occurs.
//
if ( IsListEmpty( &FsContext->Linkage )) {
//
// Do not modify the EventFileHandles list if an event
// delivery is in progress.
//
if (CnWaitForEventDelivery(&OldIrql)) {
InsertHeadList( &EventFileHandles, &FsContext->Linkage );
} else {
Status = STATUS_TIMEOUT;
}
}
if (NT_SUCCESS(Status)) {
FsContext->EventMask = EventRequest->EventMask;
FsContext->KmodeEventCallback = EventRequest->KmodeEventCallback;
}
} else if ( !IsListEmpty( &FsContext->Linkage )) {
//
// Null event mask and the fileobj on the event file obj list means
// remove this guy from the list. Zap any events that may been queued
// waiting for an IRP. Re-init the linkage to empty so we'll add them
// back on if they re-init the mask.
//
FsContext->EventMask = 0;
//
// Do not modify the EventFileHandles list if an event
// delivery is in progress. It is okay to modify this
// FsContext structure since the EventLock is held.
//
if (CnWaitForEventDelivery(&OldIrql)) {
RemoveEntryList( &FsContext->Linkage );
InitializeListHead( &FsContext->Linkage );
} else {
Status = STATUS_TIMEOUT;
}
while ( !IsListEmpty( &FsContext->EventList )) {
NextEntry = RemoveHeadList( &FsContext->EventList );
ExFreeToNPagedLookasideList( EventLookasideList, NextEntry );
}
} else {
//
// can't provide NULL event mask first time in
//
Status = STATUS_INVALID_PARAMETER_MIX;
}
CnReleaseLock( &EventLock, OldIrql );
if (Status != STATUS_SUCCESS) {
CnTrace(
EVENT_DETAIL, SetEventMaskFailed,
"[CN] CnSetEventMask failed, status %!status!.",
Status
);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return Status;
} // CnSetEventMask
VOID
CnpDeliverEvents(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Parameter
)
/*++
Routine Description:
Deliver any queued events to those who are waiting. If an IRP is already
queued, complete it with the info supplied.
Arguments:
DeviceObject - clusnet device object, not used
Parameter - PIO_WORKITEM that must be freed
Return Value:
None
--*/
{
CN_IRQL OldIrql;
PCLUSNET_EVENT_ENTRY Event;
PCLUSNET_EVENT_RESPONSE UserEventData;
PCN_FSCONTEXT FsContext;
PLIST_ENTRY NextFsHandleEntry;
PIRP EventIrp;
PLIST_ENTRY Entry;
ULONG eventsDelivered = 0;
BOOLEAN revisitRequired;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
//
// free the workitem
//
IoFreeWorkItem( (PIO_WORKITEM) Parameter );
//
// grab the cancel and event locks and loop through the file handles,
// looking to see which file objs have events queued and IRPs pending.
//
CnAcquireCancelSpinLock ( &OldIrql );
CnAcquireLockAtDpc( &EventLock );
do {
//
// Indicate that a thread is iterating through the EventFileHandles
// list to deliver events.
//
CnTrace(
EVENT_DETAIL, DeliverEventsStartIteration,
"[CN] CnpDeliverEvents: starting file handles list iteration."
);
CnStartEventDelivery();
NextFsHandleEntry = EventFileHandles.Flink;
while ( NextFsHandleEntry != &EventFileHandles ) {
FsContext = CONTAINING_RECORD( NextFsHandleEntry, CN_FSCONTEXT, Linkage );
EventIrp = FsContext->EventIrp;
if ( !IsListEmpty( &FsContext->EventList ) && EventIrp != NULL ) {
//
// clear the pointer to the pended IRP and remove the entry from the
// event list while synchronized.
//
FsContext->EventIrp = NULL;
Entry = RemoveHeadList( &FsContext->EventList );
CnReleaseLockFromDpc( &EventLock );
Event = CONTAINING_RECORD( Entry, CLUSNET_EVENT_ENTRY, Linkage );
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnDeliverEvents: completing IRP %p with event %d\n",
EventIrp, Event->EventData.EventType));
}
EventIrp->CancelIrql = OldIrql;
UserEventData = (PCLUSNET_EVENT_RESPONSE)EventIrp->AssociatedIrp.SystemBuffer;
UserEventData->Epoch = Event->EventData.Epoch;
UserEventData->EventType = Event->EventData.EventType;
UserEventData->NodeId = Event->EventData.NodeId;
UserEventData->NetworkId = Event->EventData.NetworkId;
ExFreeToNPagedLookasideList( EventLookasideList, Entry );
CnTrace(
EVENT_DETAIL, DeliverEventsCompletingIrp,
"[CN] Completing IRP to deliver event: "
"Epoch %u, Type %u, NodeId %u, NetworkId %u.",
UserEventData->Epoch,
UserEventData->EventType,
UserEventData->NodeId,
UserEventData->NetworkId
);
//
// IO Cancel lock is released in this routine
//
CnCompletePendingRequest(EventIrp,
STATUS_SUCCESS,
sizeof( CLUSNET_EVENT_RESPONSE ));
CnAcquireCancelSpinLock ( &OldIrql );
CnAcquireLockAtDpc( &EventLock );
++eventsDelivered;
}
NextFsHandleEntry = FsContext->Linkage.Flink;
}
CnTrace(
EVENT_DETAIL, DeliverEventsStopIteration,
"[CN] CnpDeliverEvents: file handle list iteration complete."
);
} while ( CnStopEventDelivery() );
CnReleaseLockFromDpc( &EventLock );
CnReleaseCancelSpinLock( OldIrql );
CnTrace(
EVENT_DETAIL, DeliverEventsSummary,
"[CN] CnpDeliverEvents: delivered %u events.",
eventsDelivered
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnDeliverEvents: events delivered %d\n", eventsDelivered ));
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
} // CnDeliverEvents
NTSTATUS
CnIssueEvent(
CLUSNET_EVENT_TYPE EventType,
CL_NODE_ID NodeId OPTIONAL,
CL_NETWORK_ID NetworkId OPTIONAL
)
/*++
Routine Description:
Post an event to each file object's event queue that is interested in this
type of event. Schedule a work queue item to run down the file objs to
deliver the events. We can't complete the IRPs directly since we might
violate the locking order inside clusnet.
Arguments:
EventType - type of event
NodeId - optional node Id associated with event
NetworkId - optional network Id associated with event
Return Value:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOUCES
--*/
{
CN_IRQL OldIrql;
PCLUSNET_EVENT_ENTRY Event;
PCLUSNET_EVENT_RESPONSE UserData;
PCN_FSCONTEXT FsContext;
PLIST_ENTRY NextFsHandleEntry;
PIRP EventIrp;
PIO_WORKITEM EventWorkItem;
BOOLEAN startupWorkerThread = FALSE;
BOOLEAN eventHandled = FALSE;
CnVerifyCpuLockMask(
0, // Required
CNP_EVENT_LOCK, // Forbidden
CNP_EVENT_LOCK_PRECEEDING // Maximum
);
CnTrace(
EVENT_DETAIL, CnIssueEvent,
"[CN] CnIssueEvent: Event Type %u, NodeId %u, NetworkId %u.",
EventType, NodeId, NetworkId
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(( "[CN] CnIssueEvent: Event type 0x%lx Node: %d Network: %d\n",
EventType, NodeId, NetworkId ));
}
//
// grab the event lock and loop through the file handles, looking to see
// which ones are interested in this event
//
CnAcquireLock( &EventLock, &OldIrql );
//
// Indicate that a thread is iterating through the EventFileHandles
// list to deliver events (kernel-mode callback counts as a delivery).
//
CnTrace(
EVENT_DETAIL, IssueEventStartIteration,
"[CN] CnIssueEvent: starting file handles list iteration."
);
CnStartEventDelivery();
NextFsHandleEntry = EventFileHandles.Flink;
if ( NextFsHandleEntry == &EventFileHandles ) {
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(( "[CN] CnIssueEvent: No file objs on event file handle list\n"));
}
}
while ( NextFsHandleEntry != &EventFileHandles ) {
FsContext = CONTAINING_RECORD( NextFsHandleEntry, CN_FSCONTEXT, Linkage );
if ( FsContext->EventMask & EventType ) {
//
// if kernel mode, then issue the callback
//
if ( FsContext->KmodeEventCallback ) {
//
// up the ref count so have a valid Flink when we return any
// potential call out
//
CnReferenceFsContext( FsContext );
CnReleaseLock( &EventLock, OldIrql );
CnTrace(
EVENT_DETAIL, IssueEventKmodeCallback,
"[CN] CnIssueEvent: invoking kernel-mode callback %p "
"for Event Type %u NodeId %u NetworkId %u.",
FsContext->KmodeEventCallback,
EventType,
NodeId,
NetworkId
);
(*FsContext->KmodeEventCallback)( EventType, NodeId, NetworkId );
CnAcquireLock( &EventLock, &OldIrql );
CnDereferenceFsContext( FsContext );
} else {
//
// post a copy of this event on the handle's list.
//
Event = ExAllocateFromNPagedLookasideList( EventLookasideList );
if ( Event == NULL ) {
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(( "[CN] CnIssueEvent: No more Event buffers!\n"));
}
CnReleaseLock( &EventLock, OldIrql );
return STATUS_INSUFFICIENT_RESOURCES;
}
Event->EventData.Epoch = InterlockedExchange( &EventEpoch, EventEpoch );
Event->EventData.EventType = EventType;
Event->EventData.NodeId = NodeId;
Event->EventData.NetworkId = NetworkId;
InsertTailList( &FsContext->EventList, &Event->Linkage );
//
// run the worker thread only if there is an IRP already queued
//
if ( FsContext->EventIrp ) {
startupWorkerThread = TRUE;
}
}
eventHandled = TRUE;
}
NextFsHandleEntry = FsContext->Linkage.Flink;
}
//
// Indicate that iteration through the EventFileHandles list
// is complete.
//
CnTrace(
EVENT_DETAIL, IssueEventStartIteration,
"[CN] CnIssueEvent: file handles list iteration complete."
);
startupWorkerThread |= CnStopEventDelivery();
CnReleaseLock( &EventLock, OldIrql );
if ( startupWorkerThread ) {
//
// schedule deliver event routine to run
//
CnTrace(
EVENT_DETAIL, IssueEventScheduleWorker,
"[CN] CnIssueEvent: scheduling worker thread."
);
EventWorkItem = IoAllocateWorkItem( CnDeviceObject );
if ( EventWorkItem != NULL ) {
IoQueueWorkItem(
EventWorkItem,
CnpDeliverEvents,
DelayedWorkQueue,
EventWorkItem
);
}
}
if ( !eventHandled ) {
CnTrace(
EVENT_DETAIL, IssueEventNoConsumers,
"[CN] CnIssueEvent: No consumers for Event Type %u Node %u Network %u.",
EventType, NodeId, NetworkId
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(( "[CN] CnIssueEvent: No consumers for Event type 0x%lx Node: %d Network: %d\n",
EventType, NodeId, NetworkId ));
}
}
CnVerifyCpuLockMask(
0, // Required
CNP_EVENT_LOCK, // Forbidden
CNP_EVENT_LOCK_PRECEEDING // Maximum
);
return STATUS_SUCCESS;
} // CnIssueEvent
VOID
CnEventIrpCancel(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
Cancellation handler for CnGetNextEvent requests.
Return Value:
None
Notes:
Called with cancel spinlock held.
Returns with cancel spinlock released.
--*/
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT fileObject;
CN_IRQL cancelIrql = Irp->CancelIrql;
PCN_FSCONTEXT FsContext = (PCN_FSCONTEXT) IrpSp->FileObject->FsContext;
CnMarkIoCancelLockAcquired();
fileObject = CnBeginCancelRoutine(Irp);
CnAcquireLockAtDpc( &EventLock );
CnReleaseCancelSpinLock(DISPATCH_LEVEL);
CnTrace(
EVENT_DETAIL, EventIrpCancel,
"[CN] Cancelling event IRP %p.",
Irp
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnEventIrpCancel: canceling %p\n", Irp ));
}
CnAssert(DeviceObject == CnDeviceObject);
//
// We can only complete the irp if it really belongs to the Event code. The
// IRP could have been completed before we acquired the Event lock.
//
if ( FsContext->EventIrp == Irp ) {
FsContext->EventIrp = NULL;
CnReleaseLock( &EventLock, cancelIrql );
CnAcquireCancelSpinLock(&(Irp->CancelIrql));
CnEndCancelRoutine(fileObject);
CnCompletePendingRequest(Irp, STATUS_CANCELLED, 0);
return;
}
CnReleaseLock( &EventLock, cancelIrql );
CnAcquireCancelSpinLock( &cancelIrql );
CnEndCancelRoutine(fileObject);
CnReleaseCancelSpinLock(cancelIrql);
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnEventIrpCancel
NTSTATUS
CnGetNextEvent(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine obtains the next event from the event list for
this file handle. If an event is queued, it completes this IRP
with the event data. Otherwise, the IRP is pended, waiting for
an event to be posted.
Return Value:
STATUS_PENDING if IRP successfully captured
STATUS_UNSUCCESSFUL if no more room in the list or IRP couldn't be
Notes:
Returns with cancel spinlock released.
--*/
{
NTSTATUS Status;
KIRQL OldIrql;
PLIST_ENTRY Entry;
PCLUSNET_EVENT_ENTRY Event;
PCN_FSCONTEXT FsContext = IrpSp->FileObject->FsContext;
PCLUSNET_EVENT_RESPONSE UserEventData = (PCLUSNET_EVENT_RESPONSE)
Irp->AssociatedIrp.SystemBuffer;
BOOLEAN DeliveryInProgress = FALSE;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
//
// acquire the IO cancel lock, then our event lock so we're synch'ed
// with regards to the state of the IRP and the event list
//
CnAcquireCancelSpinLock( &OldIrql );
CnAcquireLockAtDpc( &EventLock );
//
// check first if we have an event queued. if we have an event queued
// and there is no delivery in progress we can complete the IRP now.
// otherwise, we need to pend the IRP to avoid out-of-order delivery.
//
if ( !IsListEmpty( &FsContext->EventList )
&& !(DeliveryInProgress = CnIsEventDeliveryInProgress())
) {
//
// complete the IRP now
//
CnReleaseCancelSpinLock(DISPATCH_LEVEL);
Entry = RemoveHeadList( &FsContext->EventList );
CnReleaseLock( &EventLock, OldIrql );
Event = CONTAINING_RECORD( Entry, CLUSNET_EVENT_ENTRY, Linkage );
*UserEventData = Event->EventData;
CnTrace(
EVENT_DETAIL, GetNextEventCompletingIrp,
"[CN] Completing IRP to deliver event: "
"Epoch %u, Type %u, NodeId %u, NetworkId %u.",
UserEventData->Epoch,
UserEventData->EventType,
UserEventData->NodeId,
UserEventData->NetworkId
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnGetNextEvent: completing IRP %p with event %d\n",
Irp, Event->EventData.EventType));
}
ExFreeToNPagedLookasideList( EventLookasideList, Entry );
Irp->IoStatus.Information = sizeof(CLUSNET_EVENT_RESPONSE);
Status = STATUS_SUCCESS;
} else {
//
// make sure we have room for the new IRP
//
if ( FsContext->EventIrp ) {
CnReleaseCancelSpinLock( DISPATCH_LEVEL );
CnTrace(
EVENT_DETAIL, GetNextIrpAlreadyPending,
"[CN] CnGetNextEvent: IRP %p is already pending.",
FsContext->EventIrp
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnGetNextEvent: IRP %p is already pending\n",
FsContext->EventIrp));
}
Status = STATUS_UNSUCCESSFUL;
} else {
Status = CnMarkRequestPending( Irp, IrpSp, CnEventIrpCancel );
CnAssert( NT_SUCCESS( Status ));
CnReleaseCancelSpinLock( DISPATCH_LEVEL );
if ( NT_SUCCESS( Status )) {
//
// remember this IRP in our open file context block
//
FsContext->EventIrp = Irp;
CnTrace(
EVENT_DETAIL, GetNextEventDeliveryInProgress,
"[CN] CnGetNextEvent: pending IRP %p, "
"delivery in progress: %!bool!",
Irp, DeliveryInProgress
);
IF_CNDBG( CN_DEBUG_EVENT ) {
CNPRINT(("[CN] CnGetNextEvent: pending IRP %p\n", Irp));
}
Status = STATUS_PENDING;
}
}
CnReleaseLock(&EventLock, OldIrql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return Status;
} // CnGetNextEvent
/* end event.c */