mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
480 lines
13 KiB
480 lines
13 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
lpcqueue.c
|
|
|
|
Abstract:
|
|
|
|
Local Inter-Process Communication (LPC) queue support routines.
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 15-May-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lpcp.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,LpcpInitializePortZone)
|
|
#pragma alloc_text(PAGE,LpcpInitializePortQueue)
|
|
#pragma alloc_text(PAGE,LpcpDestroyPortQueue)
|
|
#pragma alloc_text(PAGE,LpcpExtendPortZone)
|
|
#pragma alloc_text(PAGE,LpcpAllocateFromPortZone)
|
|
#pragma alloc_text(PAGE,LpcpFreeToPortZone)
|
|
#pragma alloc_text(PAGE,LpcpSaveDataInfoMessage)
|
|
#pragma alloc_text(PAGE,LpcpFreeDataInfoMessage)
|
|
#pragma alloc_text(PAGE,LpcpFindDataInfoMessage)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
LpcpInitializePortQueue(
|
|
IN PLPCP_PORT_OBJECT Port
|
|
)
|
|
{
|
|
PLPCP_NONPAGED_PORT_QUEUE NonPagedPortQueue;
|
|
|
|
PAGED_CODE();
|
|
|
|
NonPagedPortQueue = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(LPCP_NONPAGED_PORT_QUEUE),
|
|
'troP');
|
|
if (NonPagedPortQueue == NULL) {
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
KeInitializeSemaphore( &NonPagedPortQueue->Semaphore, 0, 0x7FFFFFFF );
|
|
NonPagedPortQueue->BackPointer = Port;
|
|
Port->MsgQueue.Semaphore = &NonPagedPortQueue->Semaphore;
|
|
InitializeListHead( &Port->MsgQueue.ReceiveHead );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
VOID
|
|
LpcpDestroyPortQueue(
|
|
IN PLPCP_PORT_OBJECT Port,
|
|
IN BOOLEAN CleanupAndDestroy
|
|
)
|
|
{
|
|
PLIST_ENTRY Next, Head;
|
|
PETHREAD ThreadWaitingForReply;
|
|
PLPCP_MESSAGE Msg;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// If this port is connected to another port, then disconnect it.
|
|
// Protect this with a lock in case the other side is going away
|
|
// at the same time.
|
|
//
|
|
|
|
ExAcquireFastMutex( &LpcpLock );
|
|
|
|
if (Port->ConnectedPort != NULL) {
|
|
Port->ConnectedPort->ConnectedPort = NULL;
|
|
}
|
|
|
|
//
|
|
// If connection port, then mark name as deleted
|
|
//
|
|
|
|
if ((Port->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
|
|
Port->Flags |= PORT_NAME_DELETED;
|
|
}
|
|
|
|
//
|
|
// Walk list of threads waiting for a reply to a message sent to this port.
|
|
// Signal each thread's LpcReplySemaphore to wake them up. They will notice
|
|
// that there was no reply and return STATUS_PORT_DISCONNECTED
|
|
//
|
|
|
|
Head = &Port->LpcReplyChainHead;
|
|
Next = Head->Flink;
|
|
while (Next != NULL && Next != Head) {
|
|
ThreadWaitingForReply = CONTAINING_RECORD( Next, ETHREAD, LpcReplyChain );
|
|
Next = Next->Flink;
|
|
RemoveEntryList( &ThreadWaitingForReply->LpcReplyChain );
|
|
InitializeListHead( &ThreadWaitingForReply->LpcReplyChain );
|
|
|
|
if (!KeReadStateSemaphore( &ThreadWaitingForReply->LpcReplySemaphore )) {
|
|
|
|
//
|
|
// Thread is waiting on a message. Signal the semaphore and free the message
|
|
//
|
|
|
|
Msg = ThreadWaitingForReply->LpcReplyMessage;
|
|
if ( Msg ) {
|
|
ThreadWaitingForReply->LpcReplyMessage = NULL;
|
|
ThreadWaitingForReply->LpcReplyMessageId = 0;
|
|
LpcpFreeToPortZone( Msg, TRUE );
|
|
}
|
|
|
|
KeReleaseSemaphore( &ThreadWaitingForReply->LpcReplySemaphore,
|
|
0,
|
|
1L,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
InitializeListHead( &Port->LpcReplyChainHead );
|
|
|
|
//
|
|
// Walk list of messages queued to this port. Remove each message from
|
|
// the list and free it.
|
|
//
|
|
|
|
Head = &Port->MsgQueue.ReceiveHead;
|
|
Next = Head->Flink;
|
|
while (Next != NULL && Next != Head) {
|
|
Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
|
|
Next = Next->Flink;
|
|
InitializeListHead( &Msg->Entry );
|
|
LpcpFreeToPortZone( Msg, TRUE );
|
|
}
|
|
InitializeListHead( &Port->MsgQueue.ReceiveHead );
|
|
|
|
ExReleaseFastMutex( &LpcpLock );
|
|
|
|
if ( CleanupAndDestroy ) {
|
|
|
|
//
|
|
// Free semaphore associated with the queue.
|
|
//
|
|
if (Port->MsgQueue.Semaphore != NULL) {
|
|
ExFreePool(CONTAINING_RECORD(Port->MsgQueue.Semaphore,
|
|
LPCP_NONPAGED_PORT_QUEUE,
|
|
Semaphore));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LpcpInitializePortZone(
|
|
IN ULONG MaxEntrySize,
|
|
IN ULONG SegmentSize,
|
|
IN ULONG MaxPoolUsage
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID Segment;
|
|
PLPCP_MESSAGE Msg;
|
|
LONG SegSize;
|
|
|
|
PAGED_CODE();
|
|
LpcpZone.MaxPoolUsage = MaxPoolUsage;
|
|
LpcpZone.GrowSize = SegmentSize;
|
|
Segment = ExAllocatePoolWithTag( PagedPool, SegmentSize, 'ZcpL' );
|
|
if (Segment == NULL) {
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
KeInitializeEvent( &LpcpZone.FreeEvent, SynchronizationEvent, FALSE );
|
|
Status = ExInitializeZone( &LpcpZone.Zone,
|
|
MaxEntrySize,
|
|
Segment,
|
|
SegmentSize
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
ExFreePool( Segment );
|
|
}
|
|
|
|
SegSize = PAGE_SIZE;
|
|
LpcpTotalNumberOfMessages = 0;
|
|
Msg = (PLPCP_MESSAGE)((PZONE_SEGMENT_HEADER)Segment + 1);
|
|
while (SegSize >= (LONG)LpcpZone.Zone.BlockSize) {
|
|
Msg->ZoneIndex = (USHORT)++LpcpTotalNumberOfMessages;
|
|
Msg->Reserved0 = 0;
|
|
Msg->Request.MessageId = 0;
|
|
Msg = (PLPCP_MESSAGE)((PCHAR)Msg + LpcpZone.Zone.BlockSize);
|
|
SegSize -= LpcpZone.Zone.BlockSize;
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LpcpExtendPortZone(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID Segment;
|
|
PLPCP_MESSAGE Msg;
|
|
LARGE_INTEGER WaitTimeout;
|
|
BOOLEAN AlreadyRetried;
|
|
LONG SegmentSize;
|
|
|
|
PAGED_CODE();
|
|
AlreadyRetried = FALSE;
|
|
retry:
|
|
if (LpcpZone.Zone.TotalSegmentSize + LpcpZone.GrowSize > LpcpZone.MaxPoolUsage) {
|
|
LpcpPrint(( "Out of space in global LPC zone - current size is %08x\n",
|
|
LpcpZone.Zone.TotalSegmentSize
|
|
));
|
|
|
|
WaitTimeout.QuadPart = Int32x32To64( 120000, -10000 );
|
|
ExReleaseFastMutex( &LpcpLock );
|
|
Status = KeWaitForSingleObject( &LpcpZone.FreeEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&WaitTimeout
|
|
);
|
|
ExAcquireFastMutex( &LpcpLock );
|
|
if (Status != STATUS_SUCCESS) {
|
|
LpcpPrint(( "Error waiting for %lx->FreeEvent - Status == %X\n",
|
|
&LpcpZone,
|
|
Status
|
|
));
|
|
|
|
if ( !AlreadyRetried ) {
|
|
AlreadyRetried = TRUE;
|
|
LpcpZone.MaxPoolUsage += LpcpZone.GrowSize;
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
Segment = ExAllocatePoolWithTag( PagedPool, LpcpZone.GrowSize, 'ZcpL' );
|
|
if (Segment == NULL) {
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
Status = ExExtendZone( &LpcpZone.Zone,
|
|
Segment,
|
|
LpcpZone.GrowSize
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
ExFreePool( Segment );
|
|
}
|
|
#if DEVL
|
|
else {
|
|
LpcpTrace(("Extended LPC zone by %x for a total of %x\n",
|
|
LpcpZone.GrowSize, LpcpZone.Zone.TotalSegmentSize
|
|
));
|
|
|
|
SegmentSize = PAGE_SIZE;
|
|
Msg = (PLPCP_MESSAGE)((PZONE_SEGMENT_HEADER)Segment + 1);
|
|
while (SegmentSize >= (LONG)LpcpZone.Zone.BlockSize) {
|
|
Msg->ZoneIndex = (USHORT)++LpcpTotalNumberOfMessages;
|
|
Msg = (PLPCP_MESSAGE)((PCHAR)Msg + LpcpZone.Zone.BlockSize);
|
|
SegmentSize -= LpcpZone.Zone.BlockSize;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return( Status );
|
|
}
|
|
|
|
PLPCP_MESSAGE
|
|
FASTCALL
|
|
LpcpAllocateFromPortZone(
|
|
ULONG Size
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PLPCP_MESSAGE Msg;
|
|
|
|
PAGED_CODE();
|
|
|
|
do {
|
|
Msg = (PLPCP_MESSAGE)ExAllocateFromZone( &LpcpZone.Zone );
|
|
|
|
if (Msg != NULL) {
|
|
LpcpTrace(( "Allocate Msg %lx\n", Msg ));
|
|
InitializeListHead( &Msg->Entry );
|
|
Msg->RepliedToThread = NULL;
|
|
#if DBG
|
|
Msg->ZoneIndex |= LPCP_ZONE_MESSAGE_ALLOCATED;
|
|
#endif
|
|
return Msg;
|
|
}
|
|
|
|
LpcpTrace(( "Extending Zone %lx\n", &LpcpZone.Zone ));
|
|
Status = LpcpExtendPortZone( );
|
|
} while (NT_SUCCESS(Status));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
LpcpFreeToPortZone(
|
|
IN PLPCP_MESSAGE Msg,
|
|
IN BOOLEAN MutexOwned
|
|
)
|
|
{
|
|
BOOLEAN ZoneMemoryAvailable;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!MutexOwned) {
|
|
ExAcquireFastMutex( &LpcpLock );
|
|
}
|
|
|
|
LpcpTrace(( "Free Msg %lx\n", Msg ));
|
|
#if DBG
|
|
if (!(Msg->ZoneIndex & LPCP_ZONE_MESSAGE_ALLOCATED)) {
|
|
LpcpPrint(( "Msg %lx has already been freed.\n", Msg ));
|
|
DbgBreakPoint();
|
|
|
|
if (!MutexOwned) {
|
|
ExReleaseFastMutex( &LpcpLock );
|
|
}
|
|
return;
|
|
}
|
|
|
|
Msg->ZoneIndex &= ~LPCP_ZONE_MESSAGE_ALLOCATED;
|
|
#endif
|
|
if (!IsListEmpty( &Msg->Entry )) {
|
|
RemoveEntryList( &Msg->Entry );
|
|
InitializeListHead( &Msg->Entry );
|
|
}
|
|
|
|
if (Msg->RepliedToThread != NULL) {
|
|
ObDereferenceObject( Msg->RepliedToThread );
|
|
Msg->RepliedToThread = NULL;
|
|
}
|
|
|
|
Msg->Reserved0 = 0; // Mark as free
|
|
ZoneMemoryAvailable = (BOOLEAN)(ExFreeToZone( &LpcpZone.Zone, &Msg->FreeEntry ) == NULL);
|
|
|
|
if (!MutexOwned) {
|
|
ExReleaseFastMutex( &LpcpLock );
|
|
}
|
|
|
|
if (ZoneMemoryAvailable) {
|
|
KeSetEvent( &LpcpZone.FreeEvent,
|
|
LPC_RELEASE_WAIT_INCREMENT,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LpcpSaveDataInfoMessage(
|
|
IN PLPCP_PORT_OBJECT Port,
|
|
PLPCP_MESSAGE Msg
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ExAcquireFastMutex( &LpcpLock );
|
|
if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
|
|
Port = Port->ConnectionPort;
|
|
}
|
|
LpcpTrace(( "%s Saving DataInfo Message %lx (%u.%u) Port: %lx\n",
|
|
PsGetCurrentProcess()->ImageFileName,
|
|
Msg,
|
|
Msg->Request.MessageId,
|
|
Msg->Request.CallbackId,
|
|
Port
|
|
));
|
|
InsertTailList( &Port->LpcDataInfoChainHead, &Msg->Entry );
|
|
ExReleaseFastMutex( &LpcpLock );
|
|
}
|
|
|
|
|
|
VOID
|
|
LpcpFreeDataInfoMessage(
|
|
IN PLPCP_PORT_OBJECT Port,
|
|
IN ULONG MessageId,
|
|
IN ULONG CallbackId
|
|
)
|
|
{
|
|
PLPCP_MESSAGE Msg;
|
|
PLIST_ENTRY Head, Next;
|
|
|
|
PAGED_CODE();
|
|
if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
|
|
Port = Port->ConnectionPort;
|
|
}
|
|
Head = &Port->LpcDataInfoChainHead;
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
|
|
if (Msg->Request.MessageId == MessageId &&
|
|
Msg->Request.CallbackId == CallbackId
|
|
) {
|
|
LpcpTrace(( "%s Removing DataInfo Message %lx (%u.%u) Port: %lx\n",
|
|
PsGetCurrentProcess()->ImageFileName,
|
|
Msg,
|
|
Msg->Request.MessageId,
|
|
Msg->Request.CallbackId,
|
|
Port
|
|
));
|
|
RemoveEntryList( &Msg->Entry );
|
|
InitializeListHead( &Msg->Entry );
|
|
LpcpFreeToPortZone( Msg, TRUE );
|
|
return;
|
|
}
|
|
else {
|
|
Next = Next->Flink;
|
|
}
|
|
}
|
|
|
|
LpcpTrace(( "%s Unable to find DataInfo Message (%u.%u) Port: %lx\n",
|
|
PsGetCurrentProcess()->ImageFileName,
|
|
MessageId,
|
|
CallbackId,
|
|
Port
|
|
));
|
|
return;
|
|
}
|
|
|
|
|
|
PLPCP_MESSAGE
|
|
LpcpFindDataInfoMessage(
|
|
IN PLPCP_PORT_OBJECT Port,
|
|
IN ULONG MessageId,
|
|
IN ULONG CallbackId
|
|
)
|
|
{
|
|
PLPCP_MESSAGE Msg;
|
|
PLIST_ENTRY Head, Next;
|
|
|
|
PAGED_CODE();
|
|
if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
|
|
Port = Port->ConnectionPort;
|
|
}
|
|
Head = &Port->LpcDataInfoChainHead;
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
|
|
if (Msg->Request.MessageId == MessageId &&
|
|
Msg->Request.CallbackId == CallbackId
|
|
) {
|
|
LpcpTrace(( "%s Found DataInfo Message %lx (%u.%u) Port: %lx\n",
|
|
PsGetCurrentProcess()->ImageFileName,
|
|
Msg,
|
|
Msg->Request.MessageId,
|
|
Msg->Request.CallbackId,
|
|
Port
|
|
));
|
|
return Msg;
|
|
}
|
|
else {
|
|
Next = Next->Flink;
|
|
}
|
|
}
|
|
|
|
LpcpTrace(( "%s Unable to find DataInfo Message (%u.%u) Port: %lx\n",
|
|
PsGetCurrentProcess()->ImageFileName,
|
|
MessageId,
|
|
CallbackId,
|
|
Port
|
|
));
|
|
return NULL;
|
|
}
|