xbox-kernel/private/ntos/io/complete.c
2020-09-30 17:17:25 +02:00

517 lines
14 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 "iop.h"
#define IopFreeMiniPacket(MiniPacket) ExFreePool(MiniPacket)
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;
PVOID IoCompletion;
NTSTATUS Status;
//
// Allocate I/O completion object.
//
Status = ObCreateObject(&IoCompletionObjectType,
ObjectAttributes,
sizeof(KQUEUE),
(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,
ObjectAttributes,
0,
&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)) {
*IoCompletionHandle = Handle;
}
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtQueryIoCompletion (
IN HANDLE IoCompletionHandle,
OUT PIO_COMPLETION_BASIC_INFORMATION IoCompletionInformation
)
/*++
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.
IoCompletionInformation - Supplies a pointer to a record that receives
the requested information.
Return Value:
STATUS_SUCCESS is returned if the function is success. Otherwise, an
error status is returned.
--*/
{
PVOID IoCompletion;
LONG Depth;
NTSTATUS Status;
//
// Reference the I/O completion object by handle.
//
Status = ObReferenceObjectByHandle(IoCompletionHandle,
&IoCompletionObjectType,
&IoCompletion);
//
// 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);
IoCompletionInformation->Depth = Depth;
}
//
// 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,
&IoCompletionObjectType,
&IoCompletion);
if (NT_SUCCESS(Status)) {
Status = IoSetIoCompletion(IoCompletion,
KeyContext,
ApcContext,
IoStatus,
IoStatusInformation);
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;
NTSTATUS Status;
PVOID LocalApcContext;
PVOID LocalKeyContext;
IO_STATUS_BLOCK LocalIoStatusBlock;
PIOP_MINI_COMPLETION_PACKET MiniPacket;
//
// Get previous processor mode and probe the I/O context, status,
// and timeout if necessary.
//
CapturedTimeout = NULL;
if (ARGUMENT_PRESENT(Timeout)) {
CapturedTimeout = Timeout;
}
//
// Reference the I/O completion object by handle.
//
Status = ObReferenceObjectByHandle(IoCompletionHandle,
&IoCompletionObjectType,
&IoCompletion);
//
// 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,
KernelMode,
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;
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;
}
//
// Deference I/O completion object.
//
ObDereferenceObject(IoCompletion);
}
//
// Return service status.
//
return Status;
}
NTKERNELAPI
NTSTATUS
IoSetIoCompletion (
IN PVOID IoCompletion,
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:
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.
--*/
{
PIOP_MINI_COMPLETION_PACKET MiniPacket;
NTSTATUS Status;
PAGED_CODE();
//
// Attempt to allocate the minpacket from the per processor lookaside list.
//
MiniPacket = ExAllocatePoolWithTag(sizeof(*MiniPacket), ' pcI');
//
// If a minipacket was successfully allocated, then initialize and
// queue the packet to the specified I/O completion queue.
//
if (MiniPacket != NULL) {
MiniPacket->PacketType = IopCompletionPacketMini;
MiniPacket->KeyContext = KeyContext;
MiniPacket->ApcContext = ApcContext;
MiniPacket->IoStatus = IoStatus;
MiniPacket->IoStatusInformation = IoStatusInformation;
KeInsertQueue((PKQUEUE)IoCompletion, &MiniPacket->ListEntry);
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
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);
}
}