919 lines
26 KiB
C
919 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
complete.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the executive I/O completion object. Functions are
|
||
provided to create, open, query, and wait for I/O completion objects.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 25-Feb-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "iomgr.h"
|
||
|
||
|
||
//
|
||
// Define forward referenced function prototypes.
|
||
//
|
||
|
||
VOID
|
||
IopFreeMiniPacket (
|
||
PIOP_MINI_COMPLETION_PACKET MiniPacket
|
||
);
|
||
|
||
//
|
||
// Define section types for appropriate functions.
|
||
//
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtCreateIoCompletion)
|
||
#pragma alloc_text(PAGE, NtOpenIoCompletion)
|
||
#pragma alloc_text(PAGE, NtQueryIoCompletion)
|
||
#pragma alloc_text(PAGE, NtRemoveIoCompletion)
|
||
#pragma alloc_text(PAGE, NtSetIoCompletion)
|
||
#pragma alloc_text(PAGE, IoSetIoCompletion)
|
||
#pragma alloc_text(PAGE, IopFreeMiniPacket)
|
||
#pragma alloc_text(PAGE, IopDeleteIoCompletion)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
NtCreateIoCompletion (
|
||
IN PHANDLE IoCompletionHandle,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
||
IN ULONG Count OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates an I/O completion object, sets the maximum
|
||
target concurrent thread count to the specified value, and opens
|
||
a handle to the object with the specified desired access.
|
||
|
||
Arguments:
|
||
|
||
IoCompletionHandle - Supplies a pointer to a variable that receives
|
||
the I/O completion object handle.
|
||
|
||
DesiredAccess - Supplies the desired types of access for the I/O
|
||
completion object.
|
||
|
||
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
||
|
||
Count - Supplies the target maximum number of threads that should
|
||
be concurrently active. If this parameter is not specified, then
|
||
the number of processors is used.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the function is success. Otherwise, an
|
||
error status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
HANDLE Handle;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PVOID IoCompletion;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Establish an exception handler, probe the output handle address, and
|
||
// attempt to create an I/O completion object. If the probe fails, then
|
||
// return the exception code as the service status. Otherwise, return the
|
||
// status value returned by the object insertion routine.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe output handle address if
|
||
// necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
ProbeForWriteHandle(IoCompletionHandle);
|
||
}
|
||
|
||
//
|
||
// Allocate I/O completion object.
|
||
//
|
||
|
||
Status = ObCreateObject(PreviousMode,
|
||
IoCompletionObjectType,
|
||
ObjectAttributes,
|
||
PreviousMode,
|
||
NULL,
|
||
sizeof(KQUEUE),
|
||
0,
|
||
0,
|
||
(PVOID *)&IoCompletion);
|
||
|
||
//
|
||
// If the I/O completion object was successfully allocated, then
|
||
// initialize the object and attempt to insert it in the handle
|
||
// table of the current process.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
KeInitializeQueue((PKQUEUE)IoCompletion, Count);
|
||
Status = ObInsertObject(IoCompletion,
|
||
NULL,
|
||
DesiredAccess,
|
||
0,
|
||
(PVOID *)NULL,
|
||
&Handle);
|
||
|
||
//
|
||
// If the I/O completion object was successfully inserted in
|
||
// the handle table of the current process, then attempt to
|
||
// write the handle value. If the write attempt fails, then
|
||
// do not report an error. When the caller attempts to access
|
||
// the handle value, an access violation will occur.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
try {
|
||
*IoCompletionHandle = Handle;
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
NOTHING;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the output handle address,
|
||
// then always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Return service status.
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtOpenIoCompletion (
|
||
OUT PHANDLE IoCompletionHandle,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function opens a handle to an I/O completion object with the
|
||
specified desired access.
|
||
|
||
Arguments:
|
||
|
||
IoCompletionHandle - Supplies a pointer to a variable that receives
|
||
the completion object handle.
|
||
|
||
DesiredAccess - Supplies the desired types of access for the I/O
|
||
completion object.
|
||
|
||
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the function is success. Otherwise, an
|
||
error status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
HANDLE Handle;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Establish an exception handler, probe the output handle address,
|
||
// and attempt to open an I/O completion object. If the probe fails,
|
||
// then return the exception code as the service status. Otherwise,
|
||
// return the status value returned by the object open routine.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe output handle address if
|
||
// necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
ProbeForWriteHandle(IoCompletionHandle);
|
||
}
|
||
|
||
//
|
||
// Open handle to the completion object with the specified desired
|
||
// access.
|
||
//
|
||
|
||
Status = ObOpenObjectByName(ObjectAttributes,
|
||
IoCompletionObjectType,
|
||
PreviousMode,
|
||
NULL,
|
||
DesiredAccess,
|
||
NULL,
|
||
&Handle);
|
||
|
||
//
|
||
// If the open was successful, then attempt to write the I/O
|
||
// completion object handle value. If the write attempt fails,
|
||
// then do not report an error. When the caller attempts to
|
||
// access the handle value, an access violation will occur.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
try {
|
||
*IoCompletionHandle = Handle;
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
NOTHING;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the output handle address,
|
||
// then always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
|
||
//
|
||
// Return service status.
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtQueryIoCompletion (
|
||
IN HANDLE IoCompletionHandle,
|
||
IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
|
||
OUT PVOID IoCompletionInformation,
|
||
IN ULONG IoCompletionInformationLength,
|
||
OUT PULONG ReturnLength OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function queries the state of an I/O completion object and returns
|
||
the requested information in the specified record structure.
|
||
|
||
Arguments:
|
||
|
||
IoCompletionHandle - Supplies a handle to an I/O completion object.
|
||
|
||
IoCompletionInformationClass - Supplies the class of information being
|
||
requested.
|
||
|
||
IoCompletionInformation - Supplies a pointer to a record that receives
|
||
the requested information.
|
||
|
||
IoCompletionInformationLength - Supplies the length of the record that
|
||
receives the requested information.
|
||
|
||
ReturnLength - Supplies an optional pointer to a variable that receives
|
||
the actual length of the information that is returned.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the function is success. Otherwise, an
|
||
error status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PVOID IoCompletion;
|
||
LONG Depth;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Establish an exception handler, probe the output arguments, reference
|
||
// the I/O completion object, and return the specified information. If
|
||
// the probe fails, then return the exception code as the service status.
|
||
// Otherwise return the status value returned by the reference object by
|
||
// handle routine.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
ProbeForWriteSmallStructure(IoCompletionInformation,
|
||
sizeof(IO_COMPLETION_BASIC_INFORMATION),
|
||
sizeof(ULONG));
|
||
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
ProbeForWriteUlong(ReturnLength);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check argument validity.
|
||
//
|
||
|
||
if (IoCompletionInformationClass != IoCompletionBasicInformation) {
|
||
return STATUS_INVALID_INFO_CLASS;
|
||
}
|
||
|
||
if (IoCompletionInformationLength != sizeof(IO_COMPLETION_BASIC_INFORMATION)) {
|
||
return STATUS_INFO_LENGTH_MISMATCH;
|
||
}
|
||
|
||
//
|
||
// Reference the I/O completion object by handle.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle(IoCompletionHandle,
|
||
IO_COMPLETION_QUERY_STATE,
|
||
IoCompletionObjectType,
|
||
PreviousMode,
|
||
&IoCompletion,
|
||
NULL);
|
||
|
||
//
|
||
// If the reference was successful, then read the current state of
|
||
// the I/O completion object, dereference the I/O completion object,
|
||
// fill in the information structure, and return the structure length
|
||
// if specified. If the write of the I/O completion information or
|
||
// the return length fails, then do not report an error. When the
|
||
// caller accesses the information structure or length an access
|
||
// violation will occur.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Depth = KeReadStateQueue((PKQUEUE)IoCompletion);
|
||
ObDereferenceObject(IoCompletion);
|
||
try {
|
||
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = Depth;
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
*ReturnLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
|
||
}
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
NOTHING;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the output arguments, then
|
||
// always handle the exception and return the exception code as the status
|
||
// value.
|
||
//
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Return service status.
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtSetIoCompletion (
|
||
IN HANDLE IoCompletionHandle,
|
||
IN PVOID KeyContext,
|
||
IN PVOID ApcContext,
|
||
IN NTSTATUS IoStatus,
|
||
IN ULONG_PTR IoStatusInformation
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allows the caller to queue an Irp to an I/O completion
|
||
port and specify all of the information that is returned out the other
|
||
end using NtRemoveIoCompletion.
|
||
|
||
Arguments:
|
||
|
||
IoCompletionHandle - Supplies a handle to the io completion port
|
||
that the caller intends to queue a completion packet to
|
||
|
||
KeyContext - Supplies the key context that is returned during a call
|
||
to NtRemoveIoCompletion
|
||
|
||
ApcContext - Supplies the apc context that is returned during a call
|
||
to NtRemoveIoCompletion
|
||
|
||
IoStatus - Supplies the IoStatus->Status data that is returned during
|
||
a call to NtRemoveIoCompletion
|
||
|
||
IoStatusInformation - Supplies the IoStatus->Information data that
|
||
is returned during a call to NtRemoveIoCompletion
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the function is success. Otherwise, an
|
||
error status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID IoCompletion;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = ObReferenceObjectByHandle(IoCompletionHandle,
|
||
IO_COMPLETION_MODIFY_STATE,
|
||
IoCompletionObjectType,
|
||
KeGetPreviousMode(),
|
||
&IoCompletion,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = IoSetIoCompletion(IoCompletion,
|
||
KeyContext,
|
||
ApcContext,
|
||
IoStatus,
|
||
IoStatusInformation,
|
||
TRUE);
|
||
|
||
ObDereferenceObject(IoCompletion);
|
||
}
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
NtRemoveIoCompletion (
|
||
IN HANDLE IoCompletionHandle,
|
||
OUT PVOID *KeyContext,
|
||
OUT PVOID *ApcContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN PLARGE_INTEGER Timeout OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function removes an entry from an I/O completion object. If there
|
||
are currently no entries available, then the calling thread waits for
|
||
an entry.
|
||
|
||
Arguments:
|
||
|
||
Completion - Supplies a handle to an I/O completion object.
|
||
|
||
KeyContext - Supplies a pointer to a variable that receives the key
|
||
context that was specified when the I/O completion object was
|
||
assoicated with a file object.
|
||
|
||
ApcContext - Supplies a pointer to a variable that receives the
|
||
context that was specified when the I/O operation was issued.
|
||
|
||
IoStatus - Supplies a pointer to a variable that receives the
|
||
I/O completion status.
|
||
|
||
Timeout - Supplies a pointer to an optional time out value.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the function is success. Otherwise, an
|
||
error status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLARGE_INTEGER CapturedTimeout;
|
||
PLIST_ENTRY Entry;
|
||
PVOID IoCompletion;
|
||
PIRP Irp;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
LARGE_INTEGER TimeoutValue;
|
||
PVOID LocalApcContext;
|
||
PVOID LocalKeyContext;
|
||
IO_STATUS_BLOCK LocalIoStatusBlock;
|
||
PIOP_MINI_COMPLETION_PACKET MiniPacket;
|
||
|
||
//
|
||
// Establish an exception handler, probe the I/O context, the I/O
|
||
// status, and the optional timeout value if specified, reference
|
||
// the I/O completion object, and attempt to remove an entry from
|
||
// the I/O completion object. If the probe fails, then return the
|
||
// exception code as the service status. Otherwise, return a value
|
||
// dependent on the outcome of the queue removal.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe the I/O context, status,
|
||
// and timeout if necessary.
|
||
//
|
||
|
||
CapturedTimeout = NULL;
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
ProbeForWriteUlong_ptr((PULONG_PTR)ApcContext);
|
||
ProbeForWriteUlong_ptr((PULONG_PTR)KeyContext);
|
||
ProbeForWriteIoStatus(IoStatusBlock);
|
||
if (ARGUMENT_PRESENT(Timeout)) {
|
||
CapturedTimeout = &TimeoutValue;
|
||
TimeoutValue = ProbeAndReadLargeInteger(Timeout);
|
||
}
|
||
|
||
} else{
|
||
if (ARGUMENT_PRESENT(Timeout)) {
|
||
CapturedTimeout = Timeout;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Reference the I/O completion object by handle.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle(IoCompletionHandle,
|
||
IO_COMPLETION_MODIFY_STATE,
|
||
IoCompletionObjectType,
|
||
PreviousMode,
|
||
&IoCompletion,
|
||
NULL);
|
||
|
||
//
|
||
// If the reference was successful, then attempt to remove an entry
|
||
// from the I/O completion object. If an entry is removed from the
|
||
// I/O completion object, then capture the completion information,
|
||
// release the associated IRP, and attempt to write the completion
|
||
// inforamtion. If the write of the completion infomation fails,
|
||
// then do not report an error. When the caller attempts to access
|
||
// the completion information, an access violation will occur.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Entry = KeRemoveQueue((PKQUEUE)IoCompletion,
|
||
PreviousMode,
|
||
CapturedTimeout);
|
||
|
||
//
|
||
// N.B. The entry value returned can be the address of a list
|
||
// entry, STATUS_USER_APC, or STATUS_TIMEOUT.
|
||
//
|
||
|
||
if (((LONG_PTR)Entry == STATUS_TIMEOUT) ||
|
||
((LONG_PTR)Entry == STATUS_USER_APC)) {
|
||
Status = (NTSTATUS)((LONG_PTR)Entry);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set the completion status, capture the completion
|
||
// information, deallocate the associated IRP, and
|
||
// attempt to write the completion information.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
try {
|
||
MiniPacket = CONTAINING_RECORD(Entry,
|
||
IOP_MINI_COMPLETION_PACKET,
|
||
ListEntry);
|
||
|
||
if ( MiniPacket->PacketType == IopCompletionPacketIrp ) {
|
||
Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
LocalApcContext = Irp->Overlay.AsynchronousParameters.UserApcContext;
|
||
LocalKeyContext = (PVOID)Irp->Tail.CompletionKey;
|
||
LocalIoStatusBlock = Irp->IoStatus;
|
||
IoFreeIrp(Irp);
|
||
|
||
} else {
|
||
|
||
LocalApcContext = MiniPacket->ApcContext;
|
||
LocalKeyContext = (PVOID)MiniPacket->KeyContext;
|
||
LocalIoStatusBlock.Status = MiniPacket->IoStatus;
|
||
LocalIoStatusBlock.Information = MiniPacket->IoStatusInformation;
|
||
IopFreeMiniPacket(MiniPacket);
|
||
}
|
||
|
||
*ApcContext = LocalApcContext;
|
||
*KeyContext = LocalKeyContext;
|
||
*IoStatusBlock = LocalIoStatusBlock;
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
NOTHING;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Deference I/O completion object.
|
||
//
|
||
|
||
ObDereferenceObject(IoCompletion);
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the previous count, then
|
||
// always handle the exception and return the exception code as the status
|
||
// value.
|
||
//
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Return service status.
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTKERNELAPI
|
||
NTSTATUS
|
||
IoSetIoCompletion (
|
||
IN PVOID IoCompletion,
|
||
IN PVOID KeyContext,
|
||
IN PVOID ApcContext,
|
||
IN NTSTATUS IoStatus,
|
||
IN ULONG_PTR IoStatusInformation,
|
||
IN BOOLEAN Quota
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allows the caller to queue an Irp to an I/O completion
|
||
port and specify all of the information that is returned out the other
|
||
end using NtRemoveIoCompletion.
|
||
|
||
Arguments:
|
||
|
||
IoCompletion - Supplies a a pointer to the completion port that the caller
|
||
intends to queue a completion packet to.
|
||
|
||
KeyContext - Supplies the key context that is returned during a call
|
||
to NtRemoveIoCompletion.
|
||
|
||
ApcContext - Supplies the apc context that is returned during a call
|
||
to NtRemoveIoCompletion.
|
||
|
||
IoStatus - Supplies the IoStatus->Status data that is returned during
|
||
a call to NtRemoveIoCompletion.
|
||
|
||
IoStatusInformation - Supplies the IoStatus->Information data that
|
||
is returned during a call to NtRemoveIoCompletion.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the function is success. Otherwise, an
|
||
error status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PGENERAL_LOOKASIDE Lookaside;
|
||
PIOP_MINI_COMPLETION_PACKET MiniPacket;
|
||
ULONG PacketType;
|
||
PKPRCB Prcb;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Attempt to allocate the minpacket from the per processor lookaside list.
|
||
//
|
||
|
||
PacketType = IopCompletionPacketMini;
|
||
Prcb = KeGetCurrentPrcb();
|
||
Lookaside = Prcb->PPLookasideList[LookasideCompletionList].P;
|
||
Lookaside->TotalAllocates += 1;
|
||
MiniPacket = (PVOID)InterlockedPopEntrySList(&Lookaside->ListHead);
|
||
|
||
//
|
||
// If the per processor lookaside list allocation failed, then attempt to
|
||
// allocate from the system lookaside list.
|
||
//
|
||
|
||
if (MiniPacket == NULL) {
|
||
Lookaside->AllocateMisses += 1;
|
||
Lookaside = Prcb->PPLookasideList[LookasideCompletionList].L;
|
||
Lookaside->TotalAllocates += 1;
|
||
MiniPacket = (PVOID)InterlockedPopEntrySList(&Lookaside->ListHead);
|
||
}
|
||
|
||
//
|
||
// If both lookaside allocation attempts failed, then attempt to allocate
|
||
// from pool.
|
||
//
|
||
|
||
if (MiniPacket == NULL) {
|
||
Lookaside->AllocateMisses += 1;
|
||
|
||
//
|
||
// If quota is specified, then allocate pool with quota charged.
|
||
// Otherwise, allocate pool without quota.
|
||
//
|
||
|
||
if (Quota != FALSE) {
|
||
PacketType = IopCompletionPacketQuota;
|
||
try {
|
||
MiniPacket = ExAllocatePoolWithQuotaTag(NonPagedPool,
|
||
sizeof(*MiniPacket),
|
||
' pcI');
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
NOTHING;
|
||
}
|
||
|
||
} else {
|
||
MiniPacket = ExAllocatePoolWithTagPriority(NonPagedPool,
|
||
sizeof(*MiniPacket),
|
||
' pcI',
|
||
LowPoolPriority);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If a minipacket was successfully allocated, then initialize and
|
||
// queue the packet to the specified I/O completion queue.
|
||
//
|
||
|
||
if (MiniPacket != NULL) {
|
||
MiniPacket->PacketType = PacketType;
|
||
MiniPacket->KeyContext = KeyContext;
|
||
MiniPacket->ApcContext = ApcContext;
|
||
MiniPacket->IoStatus = IoStatus;
|
||
MiniPacket->IoStatusInformation = IoStatusInformation;
|
||
KeInsertQueue((PKQUEUE)IoCompletion, &MiniPacket->ListEntry);
|
||
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
IopFreeMiniPacket (
|
||
PIOP_MINI_COMPLETION_PACKET MiniPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function free the specefied I/O completion packet.
|
||
|
||
Arguments:
|
||
|
||
MiniPacket - Supplies a pointer to an I/O completion minipacket.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PGENERAL_LOOKASIDE Lookaside;
|
||
PKPRCB Prcb;
|
||
|
||
//
|
||
// If the minipacket cannot be returned to either the per processor or
|
||
// system lookaside list, then free the minipacket to pool. Otherwise,
|
||
// release the quota if quota was allocated and push the entry onto
|
||
// one of the lookaside lists.
|
||
//
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
Lookaside = Prcb->PPLookasideList[LookasideCompletionList].P;
|
||
Lookaside->TotalFrees += 1;
|
||
if (ExQueryDepthSList(&Lookaside->ListHead) >= Lookaside->Depth) {
|
||
Lookaside->FreeMisses += 1;
|
||
Lookaside = Prcb->PPLookasideList[LookasideCompletionList].L;
|
||
Lookaside->TotalFrees += 1;
|
||
if (ExQueryDepthSList(&Lookaside->ListHead) >= Lookaside->Depth) {
|
||
Lookaside->FreeMisses += 1;
|
||
ExFreePool(MiniPacket);
|
||
|
||
} else {
|
||
if (MiniPacket->PacketType == IopCompletionPacketQuota) {
|
||
ExReturnPoolQuota(MiniPacket);
|
||
}
|
||
|
||
InterlockedPushEntrySList(&Lookaside->ListHead,
|
||
(PSINGLE_LIST_ENTRY)MiniPacket);
|
||
}
|
||
|
||
} else {
|
||
if (MiniPacket->PacketType == IopCompletionPacketQuota) {
|
||
ExReturnPoolQuota(MiniPacket);
|
||
}
|
||
|
||
InterlockedPushEntrySList(&Lookaside->ListHead,
|
||
(PSINGLE_LIST_ENTRY)MiniPacket);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
IopDeleteIoCompletion (
|
||
IN PVOID Object
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the delete routine for I/O completion objects. Its
|
||
function is to release all the entries in the repsective completion
|
||
queue and to rundown all threads that are current associated.
|
||
|
||
Arguments:
|
||
|
||
Object - Supplies a pointer to an executive I/O completion object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY FirstEntry;
|
||
PIRP Irp;
|
||
PLIST_ENTRY NextEntry;
|
||
PIOP_MINI_COMPLETION_PACKET MiniPacket;
|
||
|
||
//
|
||
// Rundown threads associated with the I/O completion object and get
|
||
// the list of unprocessed I/O completion IRPs.
|
||
//
|
||
|
||
FirstEntry = KeRundownQueue((PKQUEUE)Object);
|
||
if (FirstEntry != NULL) {
|
||
NextEntry = FirstEntry;
|
||
do {
|
||
MiniPacket = CONTAINING_RECORD(NextEntry,
|
||
IOP_MINI_COMPLETION_PACKET,
|
||
ListEntry);
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
if (MiniPacket->PacketType == IopCompletionPacketIrp) {
|
||
Irp = CONTAINING_RECORD(MiniPacket, IRP, Tail.Overlay.ListEntry);
|
||
IoFreeIrp(Irp);
|
||
|
||
} else {
|
||
IopFreeMiniPacket(MiniPacket);
|
||
}
|
||
|
||
} while (FirstEntry != NextEntry);
|
||
}
|
||
|
||
return;
|
||
}
|