IoCompleteRequest的实现逻辑

来源:互联网 发布:java入门视频百度网盘 编辑:程序博客网 时间:2024/04/30 11:03
#define IoCompleteRequest(a,b)  \
        IofCompleteRequest(a,b)

VOID
FASTCALL
IopfCompleteRequest(
    IN PIRP Irp,
    IN CCHAR PriorityBoost
    )


/*++


Routine Description:


    This routine is invoked to complete an I/O request.  It is invoked by the
    driver in its DPC routine to perform the final completion of the IRP.  The
    functions performed by this routine are as follows.


        1.  A check is made to determine whether the packet's stack locations
            have been exhausted.  If not, then the stack location pointer is set
            to the next location and if there is a routine to be invoked, then
            it will be invoked.  This continues until there are either no more
            routines which are interested or the packet runs out of stack.


            If a routine is invoked to complete the packet for a specific driver
            which needs to perform work a lot of work or the work needs to be
            performed in the context of another process, then the routine will
            return an alternate success code of STATUS_MORE_PROCESSING_REQUIRED.
            This indicates that this completion routine should simply return to
            its caller because the operation will be "completed" by this routine
            again sometime in the future.


        2.  A check is made to determine whether this IRP is an associated IRP.
            If it is, then the count on the master IRP is decremented.  If the
            count for the master becomes zero, then the master IRP will be
            completed according to the steps below taken for a normal IRP being
            completed.  If the count is still non-zero, then this IRP (the one
            being completed) will simply be deallocated.


        3.  If this is paging I/O or a close operation, then simply write the
            I/O status block and set the event to the signaled state, and
            dereference the event.  If this is paging I/O, deallocate the IRP
            as well.


        4.  Unlock the pages, if any, specified by the MDL by calling
            MmUnlockPages.


        5.  A check is made to determine whether or not completion of the
            request can be deferred until later.  If it can be, then this
            routine simply exits and leaves it up to the originator of the
            request to fully complete the IRP.  By not initializing and queueing
            the special kernel APC to the calling thread (which is the current
            thread by definition), a lot of interrupt and queueing processing
            can be avoided.




        6.  The final rundown routine is invoked to queue the request packet to
            the target (requesting) thread as a special kernel mode APC.


Arguments:


    Irp - Pointer to the I/O Request Packet to complete.


    PriorityBoost - Supplies the amount of priority boost that is to be given
        to the target thread when the special kernel APC is queued.


Return Value:


    None.


--*/


#define ZeroIrpStackLocation( IrpSp ) {         \
    (IrpSp)->MinorFunction = 0;                 \
    (IrpSp)->Flags = 0;                         \
    (IrpSp)->Control &= SL_ERROR_RETURNED ;     \
    (IrpSp)->Parameters.Others.Argument1 = 0;   \
    (IrpSp)->Parameters.Others.Argument2 = 0;   \
    (IrpSp)->Parameters.Others.Argument3 = 0;   \
    (IrpSp)->FileObject = (PFILE_OBJECT) NULL; }


{
    PIRP masterIrp;
    NTSTATUS status;
    PIO_STACK_LOCATION stackPointer;
    PIO_STACK_LOCATION bottomSp;
    PDEVICE_OBJECT deviceObject;
    PMDL mdl;
    PETHREAD thread;
    PFILE_OBJECT fileObject;
    KIRQL irql;
    PVOID saveAuxiliaryPointer = NULL;
    NTSTATUS    errorStatus;


    //
    // Begin by ensuring that this packet has not already been completed
    // by someone.
    //


    if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1) ||
        Irp->Type != IO_TYPE_IRP) {
        KeBugCheckEx( MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG_PTR) Irp, __LINE__, 0, 0 );
    }


    //
    // Ensure that the packet being completed really is still an IRP.
    //


    ASSERT( Irp->Type == IO_TYPE_IRP );


    //
    // Ensure that no one believes that this request is still in a cancelable
    // state.
    //


    ASSERT( !Irp->CancelRoutine );


    //
    // Ensure that the packet is not being completed with a thoroughly
    // confusing status code.  Actually completing a packet with a pending
    // status probably means that someone forgot to set the real status in
    // the packet.
    //


    ASSERT( Irp->IoStatus.Status != STATUS_PENDING );


    //
    // Ensure that the packet is not being completed with a minus one.  This
    // is apparently a common problem in some drivers, and has no meaning
    // as a status code.
    //


    ASSERT( Irp->IoStatus.Status != 0xffffffff );


    //
    // Diagnosability support.
    //


    bottomSp = ((PIO_STACK_LOCATION) ((UCHAR *) (Irp) + sizeof( IRP )));


    if (bottomSp->Control & SL_ERROR_RETURNED) {
        errorStatus = (NTSTATUS)(ULONG_PTR)(bottomSp->Parameters.Others.Argument4);
    } else {
        errorStatus = STATUS_SUCCESS;
    }


    //
    // Now check to see whether this is the last driver that needs to be
    // invoked for this packet.  If not, then bump the stack and check to
    // see whether the driver wishes to see the completion.  As each stack
    // location is examined, invoke any routine which needs to be invoked.
    // If the routine returns STATUS_MORE_PROCESSING_REQUIRED, then stop the
    // processing of this packet.
    //


    for (stackPointer = IoGetCurrentIrpStackLocation( Irp ),
         Irp->CurrentLocation++,
         Irp->Tail.Overlay.CurrentStackLocation++;
         Irp->CurrentLocation <= (CCHAR) (Irp->StackCount + 1);
         stackPointer++,
         Irp->CurrentLocation++,
         Irp->Tail.Overlay.CurrentStackLocation++) {


        //
        // A stack location was located.  Check to see whether or not it
        // has a completion routine and if so, whether or not it should be
        // invoked.
        //
        // Begin by saving the pending returned flag in the current stack
        // location in the fixed part of the IRP.
        //


        Irp->PendingReturned = stackPointer->Control & SL_PENDING_RETURNED;


        //
        // If a completion routine changed the status then
        // mark the upper level stack pointer as the one
        // that flagged the error.
        //


        if (!NT_SUCCESS(Irp->IoStatus.Status)) {


            if (Irp->IoStatus.Status != errorStatus) {
                errorStatus = Irp->IoStatus.Status;
                stackPointer->Control |= SL_ERROR_RETURNED;
                bottomSp->Parameters.Others.Argument4 = (PVOID)(ULONG_PTR)errorStatus;
                bottomSp->Control |= SL_ERROR_RETURNED; // Mark that there is status in this location
            }
        }


        if ( (NT_SUCCESS( Irp->IoStatus.Status ) &&
             stackPointer->Control & SL_INVOKE_ON_SUCCESS) ||
             (!NT_SUCCESS( Irp->IoStatus.Status ) &&
             stackPointer->Control & SL_INVOKE_ON_ERROR) ||
             (Irp->Cancel &&
             stackPointer->Control & SL_INVOKE_ON_CANCEL)
           ) {


            //
            // This driver has specified a completion routine.  Invoke the
            // routine passing it a pointer to its device object and the
            // IRP that is being completed.
            //


            ZeroIrpStackLocation( stackPointer );


            if (Irp->CurrentLocation == (CCHAR) (Irp->StackCount + 1)) {
                deviceObject = NULL;
            }
            else {
                deviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject;
            }


            status = stackPointer->CompletionRoutine( deviceObject,
                                                      Irp,
                                                      stackPointer->Context );


            if (status == STATUS_MORE_PROCESSING_REQUIRED) {


                //
                // Note:  Notice that if the driver has returned the above
                //        status value, it may have already DEALLOCATED the
                //        packet!  Therefore, do NOT touch any part of the
                //        IRP in the following code.
                //


                return;
            }


        } else {
            if (Irp->PendingReturned && Irp->CurrentLocation <= Irp->StackCount) {
                IoMarkIrpPending( Irp );
            }
            ZeroIrpStackLocation( stackPointer );
        }
    }


    //
    // Check to see whether this is an associated IRP.  If so, then decrement
    // the count in the master IRP.  If the count is decremented to zero,
    // then complete the master packet as well.
    //


    if (Irp->Flags & IRP_ASSOCIATED_IRP) {
        ULONG count;


        masterIrp = Irp->AssociatedIrp.MasterIrp;


        //
        // After this decrement master IRP cannot be touched except if count == 1.
        //


        count = IopInterlockedDecrementUlong( LockQueueIoDatabaseLock,
                                              &masterIrp->AssociatedIrp.IrpCount );


        //
        // Deallocate this packet and any MDLs that are associated with it
        // by either doing direct deallocations if they were allocated from
        // a zone or by queueing the packet to a thread to perform the
        // deallocation.
        //
        // Also, check the count of the master IRP to determine whether or not
        // the count has gone to zero.  If not, then simply get out of here.
        // Otherwise, complete the master packet.
        //


        IopFreeIrpAndMdls( Irp );
        if (count == 1) {
            IoCompleteRequest( masterIrp, PriorityBoost );
        }
        return;
    }


    //
    // Check to see if we have a name junction. If so set the stage to
    // transmogrify the reparse point data in IopCompleteRequest.
    //


    if ((Irp->IoStatus.Status == STATUS_REPARSE )  &&
        (Irp->IoStatus.Information > IO_REPARSE_TAG_RESERVED_RANGE)) {


        if (Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT) {


            //
            // For name junctions, we save the pointer to the auxiliary
            // buffer and use it below.
            //


            ASSERT( Irp->Tail.Overlay.AuxiliaryBuffer != NULL );


            saveAuxiliaryPointer = (PVOID) Irp->Tail.Overlay.AuxiliaryBuffer;


            //
            // We NULL the entry to avoid its de-allocation at this time.
            // This buffer get deallocated in IopDoNameTransmogrify
            //


            Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
        } else {


            //
            // Fail the request. A driver needed to act on this IRP prior
            // to getting to this point.
            //


            Irp->IoStatus.Status = STATUS_IO_REPARSE_TAG_NOT_HANDLED;
        }
    }


    //
    // Check the auxiliary buffer pointer in the packet and if a buffer was
    // allocated, deallocate it now.  Note that this buffer must be freed
    // here since the pointer is overlayed with the APC that will be used
    // to get to the requesting thread's context.
    //


    if (Irp->Tail.Overlay.AuxiliaryBuffer) {
        ExFreePool( Irp->Tail.Overlay.AuxiliaryBuffer );
        Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
    }


    //
    // Check to see if this is paging I/O or a close operation.  If either,
    // then special processing must be performed.  The reasons that special
    // processing must be performed is different based on the type of
    // operation being performed.  The biggest reasons for special processing
    // on paging operations are that using a special kernel APC for an in-
    // page operation cannot work since the special kernel APC can incur
    // another pagefault.  Likewise, all paging I/O uses MDLs that belong
    // to the memory manager, not the I/O system.
    //
    // Close operations are special because the close may have been invoked
    // because of a special kernel APC (some IRP was completed which caused
    // the reference count on the object to become zero while in the I/O
    // system's special kernel APC routine).  Therefore, a special kernel APC
    // cannot be used since it cannot execute until the close APC finishes.
    //
    // The special steps are as follows for a synchronous paging operation
    // and close are:
    //
    //     1.  Copy the I/O status block (it is in SVAS, nonpaged).
    //     2.  Signal the event
    //     3.  If paging I/O, deallocate the IRP
    //
    // The special steps taken for asynchronous paging operations (out-pages)
    // are as follows:
    //
    //     1.  Initialize a special kernel APC just for page writes.
    //     1.  Queue the special kernel APC.
    //
    // It should also be noted that the logic for completing a Mount request
    // operation is exactly the same as a Page Read.  No assumptions should be
    // made here about this being a Page Read operation w/o carefully checking
    // to ensure that they are also true for a Mount.  That is:
    //
    //     IRP_PAGING_IO  and  IRP_MOUNT_COMPLETION
    //
    // are the same flag in the IRP.
    //
    // Also note that the last time the IRP is touched for a close operation
    // must be just before the event is set to the signaled state.  Once this
    // occurs, the IRP can be deallocated by the thread waiting for the event.
    //
    //
    // IRP_CLOSE_OPERATION and IRP_SET_USER_EVENT are the same flags. They both indicate
    // that only the user event field should be set and no APC should be queued. Unfortunately
    // IRP_CLOSE_OPERATION is used by some drivers to do exactly this so it cannot be renamed.
    //
    //


    if (Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION |IRP_SET_USER_EVENT)) {
        if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION |IRP_SET_USER_EVENT)) {
            ULONG flags;


            flags = Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO|IRP_PAGING_IO);
            *Irp->UserIosb = Irp->IoStatus;
            (VOID) KeSetEvent( Irp->UserEvent, PriorityBoost, FALSE );
            if (flags) {
                if (IopIsReserveIrp(Irp)) {
                    IopFreeReserveIrp(PriorityBoost);
                } else {
                    IoFreeIrp( Irp );
                }
            }
        } else {
            thread = Irp->Tail.Overlay.Thread;
            KeInitializeApc( &Irp->Tail.Apc,
                             &thread->Tcb,
                             Irp->ApcEnvironment,
                             IopCompletePageWrite,
                             (PKRUNDOWN_ROUTINE) NULL,
                             (PKNORMAL_ROUTINE) NULL,
                             KernelMode,
                             (PVOID) NULL );
            (VOID) KeInsertQueueApc( &Irp->Tail.Apc,
                                     (PVOID) NULL,
                                     (PVOID) NULL,
                                     PriorityBoost );
        }
        return;
    }


    //
    // Check to see whether any pages need to be unlocked.
    //


    if (Irp->MdlAddress != NULL) {


        //
        // Unlock any pages that may be described by MDLs.
        //


        mdl = Irp->MdlAddress;
        while (mdl != NULL) {
            MmUnlockPages( mdl );
            mdl = mdl->Next;
        }
    }


    //
    // Make a final check here to determine whether or not this is a
    // synchronous I/O operation that is being completed in the context
    // of the original requestor.  If so, then an optimal path through
    // I/O completion can be taken.
    //


    if (Irp->Flags & IRP_DEFER_IO_COMPLETION && !Irp->PendingReturned) {


        if ((Irp->IoStatus.Status == STATUS_REPARSE )  &&
            (Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT)) {


            //
            // For name junctions we reinstate the address of the appropriate
            // buffer. It is freed in parse.c
            //


            Irp->Tail.Overlay.AuxiliaryBuffer = saveAuxiliaryPointer;
        }


        return;
    }


    //
    // Finally, initialize the IRP as an APC structure and queue the special
    // kernel APC to the target thread.
    //


    thread = Irp->Tail.Overlay.Thread;
    fileObject = Irp->Tail.Overlay.OriginalFileObject;


    if (!Irp->Cancel) {


        KeInitializeApc( &Irp->Tail.Apc,
                         &thread->Tcb,
                         Irp->ApcEnvironment,
                         IopCompleteRequest,
                         IopAbortRequest,
                         (PKNORMAL_ROUTINE) NULL,
                         KernelMode,
                         (PVOID) NULL );


        (VOID) KeInsertQueueApc( &Irp->Tail.Apc,
                                 fileObject,
                                 (PVOID) saveAuxiliaryPointer,
                                 PriorityBoost );
    } else {


        //
        // This request has been cancelled.  Ensure that access to the thread
        // is synchronized, otherwise it may go away while attempting to get
        // through the remainder of completion for this request.  This happens
        // when the thread times out waiting for the request to be completed
        // once it has been cancelled.
        //
        // Note that it is safe to capture the thread pointer above, w/o having
        // the lock because the cancel flag was not set at that point, and
        // the code that disassociates IRPs must set the flag before looking to
        // see whether or not the packet has been completed, and this packet
        // will appear to be completed because it no longer belongs to a driver.
        //


        irql = KeAcquireQueuedSpinLock( LockQueueIoCompletionLock );


        thread = Irp->Tail.Overlay.Thread;


        if (thread) {


            KeInitializeApc( &Irp->Tail.Apc,
                             &thread->Tcb,
                             Irp->ApcEnvironment,
                             IopCompleteRequest,
                             IopAbortRequest,
                             (PKNORMAL_ROUTINE) NULL,
                             KernelMode,
                             (PVOID) NULL );


            (VOID) KeInsertQueueApc( &Irp->Tail.Apc,
                                     fileObject,
                                     (PVOID) saveAuxiliaryPointer,
                                     PriorityBoost );


            KeReleaseQueuedSpinLock( LockQueueIoCompletionLock, irql );


        } else {


            //
            // This request has been aborted from completing in the caller's
            // thread.  This can only occur if the packet was cancelled, and
            // the driver did not complete the request, so it was timed out.
            // Attempt to drop things on the floor, since the originating thread
            // has probably exited at this point.
            //


            KeReleaseQueuedSpinLock( LockQueueIoCompletionLock, irql );


            ASSERT( Irp->Cancel );


            //
            // Drop the IRP on the floor.
            //


            IopDropIrp( Irp, fileObject );


        }
    }
}
0 0