|
|
/*++
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; }
|