Main Page | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

queue.c

Go to the documentation of this file.
00001 /*
00002  *  This program is free software; you can redistribute it and/or
00003  *  modify it under the terms of the GNU General Public License
00004  *  as published by the Free Software Foundation; either version
00005  *  2 of the License, or (at your option) any later version.
00006  *
00007  *  Copyright 2004 Spiro Trikaliotis
00008  *
00009  */
00010 
00020 #include <wdm.h>
00021 #include "cbm_driver.h"
00022 
00023 /*
00024  This file contains the quite tricky part of the drivers.
00025 */
00026 
00047 static VOID
00048 InsertIrp(IN PIO_CSQ Csq, IN PIRP Irp)
00049 {
00050     PQUEUE queue;
00051 
00052     FUNC_ENTER();
00053 
00054     PERF_EVENT_VERBOSE(0x3000, (ULONG)Irp);
00055 
00056     // Get the QUEUE object which contains this CSQ
00057 
00058     // no IRQL restrictions apply
00059     queue = CONTAINING_RECORD(Csq, QUEUE, IrpQueue);
00060 
00061     // Insert the IRP into the QUEUE
00062 
00063     // no IRQL restrictions apply
00064     InsertTailList(&queue->IrpListHead, &Irp->Tail.Overlay.ListEntry);
00065 
00066     PERF_EVENT_VERBOSE(0x3001, 0);
00067 
00068     FUNC_LEAVE();
00069 }
00070 
00091 static VOID
00092 RemoveIrp(IN PIO_CSQ Csq, IN PIRP Irp)
00093 {
00094     FUNC_ENTER();
00095 
00096     UNREFERENCED_PARAMETER(Csq);
00097 
00098     PERF_EVENT_VERBOSE(0x3010, (ULONG)Irp);
00099 
00100     // Remove the IRP from the CSQ/QUEUE
00101 
00102     // no IRQL restrictions apply
00103     RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
00104 
00105     PERF_EVENT_VERBOSE(0x3011, 0);
00106 
00107     FUNC_LEAVE();
00108 }
00109 
00149 static PIRP
00150 PeekNextIrp(IN PIO_CSQ Csq, IN PIRP Irp, IN PVOID FileObject)
00151 {
00152     PLIST_ENTRY currentListEntry;
00153     PQUEUE queue;
00154     PIRP currentIrp;
00155 
00156     FUNC_ENTER();
00157 
00158     // Get the QUEUE object which contains this CSQ
00159 
00160     // no IRQL restrictions apply
00161     queue = CONTAINING_RECORD(Csq, QUEUE, IrpQueue);
00162     
00163     // Get the list entry of the first IRP to be checked 
00164     // into currentListEntry
00165 
00166     if (Irp == NULL)
00167     {
00168         // No previous IRP was given, thus, start at the
00169         // beginning of the queue
00170 
00171         currentListEntry = queue->IrpListHead.Flink;
00172     }
00173     else
00174     {
00175         // As an IRP was given, go to its successor.
00176 
00177         currentListEntry = Irp->Tail.Overlay.ListEntry.Flink;
00178     }
00179 
00180     // only process the list if it is not empty!
00181 
00182     if (currentListEntry != &queue->IrpListHead)
00183     {
00184         // get the containing IRP
00185 
00186         // no IRQL restrictions apply
00187         currentIrp = CONTAINING_RECORD(currentListEntry, IRP, Tail.Overlay.ListEntry);
00188 
00189         // If we were not given any file object, we have finished.
00190         // If we were given a file object, check if the IRP is
00191         // associated with that file object, thus, perform some
00192         // additional steps:
00193 
00194         if (FileObject)
00195         {
00196             BOOLEAN found;
00197 
00198             // We have not found any matching IRP yet
00199 
00200             found = FALSE;
00201 
00202             // For every IRP in the list, check if the condition
00203             // holds. If we found a matching IRP, quit this loop.
00204 
00205             while ((currentListEntry != &queue->IrpListHead) && !found)
00206             {
00207                 PIO_STACK_LOCATION irpStack;
00208 
00209                 // get the containing IRP
00210 
00211                 // no IRQL restrictions apply
00212                 irpStack = IoGetCurrentIrpStackLocation(currentIrp);
00213     
00214                 if (irpStack->FileObject == (PFILE_OBJECT) FileObject)
00215                 {
00216                     found = TRUE;
00217                 }
00218                 else
00219                 {
00220                     // Proceed to next list entry (and IRP)
00221 
00222                     currentListEntry = currentListEntry->Flink;
00223 
00224                     // no IRQL restrictions apply
00225                     currentIrp = CONTAINING_RECORD(currentListEntry, IRP, 
00226                         Tail.Overlay.ListEntry);
00227                 }
00228             }
00229 
00230             if (!found)
00231             {
00232                 // If we are to quit the loop now, we have not
00233                 // found a matching IRP
00234 
00235                 currentIrp = NULL;
00236             }
00237         }
00238     }
00239     else
00240     {
00241         // We have not found any IRP
00242 
00243         currentIrp = NULL;
00244     }
00245 
00246 
00247     FUNC_LEAVE_PTR(currentIrp, PIRP);
00248 }
00249 
00269 static VOID
00270 AcquireLock(IN PIO_CSQ Csq, OUT PKIRQL Irql)
00271 {
00272     PQUEUE queue;
00273     KIRQL irql; // do not use Irql directly, but only indirectly,
00274                 // as suggested by Doron Holan at
00275                 // http://blogs.msdn.com/doronh/archive/2006/03/08/546934.aspx
00276 
00277     FUNC_ENTER();
00278 
00279     // Get the containing QUEUE object
00280 
00281     // no IRQL restrictions apply
00282     queue = CONTAINING_RECORD(Csq, QUEUE, IrpQueue);
00283 
00284     // As we are using a spin lock, acquire that one
00285 
00286     DBG_IRQL( <= DISPATCH_LEVEL);
00287     KeAcquireSpinLock(&queue->IrpListSpinlock, &irql);
00288 
00289     *Irql = irql;
00290 
00291     FUNC_LEAVE();
00292 }
00293 
00314 static VOID
00315 ReleaseLock(IN PIO_CSQ Csq, IN KIRQL Irql)
00316 {
00317     PQUEUE queue;
00318 
00319     FUNC_ENTER();
00320 
00321     // Get the containing QUEUE object
00322 
00323     // no IRQL restrictions apply
00324     queue = CONTAINING_RECORD(Csq, QUEUE, IrpQueue);
00325 
00326     // As we are using a spin lock, release that one
00327 
00328     DBG_IRQL( == DISPATCH_LEVEL);
00329     KeReleaseSpinLock(&queue->IrpListSpinlock, Irql);
00330 
00331     FUNC_LEAVE();
00332 }
00333 
00356 static VOID
00357 CompleteCanceledIrp(IN PIO_CSQ Csq, IN PIRP Irp)
00358 {
00359     PQUEUE queue;
00360 
00361     FUNC_ENTER();
00362 
00363     PERF_EVENT_CANCELIRP(Irp);
00364 
00365     // Get the containing QUEUE object
00366 
00367     // no IRQL restrictions apply
00368     queue = CONTAINING_RECORD(Csq, QUEUE, IrpQueue);
00369 
00370     // Complete the IRP
00371 
00372     QueueCompleteIrp(queue, Irp, STATUS_CANCELLED, 0);
00373 
00374     FUNC_LEAVE();
00375 }
00376 
00388 VOID
00389 QueueInit(PQUEUE Queue, PCBMDRIVER_STARTIO DriverStartIo)
00390 {
00391     FUNC_ENTER();
00392 
00393     // can be run at arbitrary IRQL
00394     KeInitializeSpinLock(&Queue->IrpListSpinlock);
00395 
00396     // can be run at arbitrary IRQL
00397     InitializeListHead(&Queue->IrpListHead);
00398 
00399     // Initialize the event which is used to wake up the thread
00400     DBG_IRQL( == PASSIVE_LEVEL);
00401     KeInitializeEvent(&Queue->NotEmptyEvent, SynchronizationEvent, FALSE);
00402 
00403 #ifdef USE_FAST_START_THREAD
00404 
00405     // Initialize the event which is used to wake up "the caller of the thread"
00406     DBG_IRQL( == PASSIVE_LEVEL);
00407     KeInitializeEvent(&Queue->BackSignalEvent, SynchronizationEvent, FALSE);
00408 
00409 #endif // #ifdef USE_FAST_START_THREAD
00410 
00411     // Initialize the cancel-safe queue
00412     // no IRQL restrictions apply!
00413     IoCsqInitialize(&Queue->IrpQueue,
00414         InsertIrp,
00415         RemoveIrp,
00416         PeekNextIrp,
00417         AcquireLock,
00418         ReleaseLock,
00419         CompleteCanceledIrp);
00420 
00421     // remember the function pointers
00422     Queue->DriverStartIo = DriverStartIo;
00423 
00424     // start in stalled state
00425     Queue->IsStalled = TRUE;
00426 
00427     // do not start with dropping IRPs
00428     Queue->IsDropping = FALSE;
00429 
00430     FUNC_LEAVE();
00431 }
00432 
00467 NTSTATUS
00468 QueueStartPacket(PQUEUE Queue, PIRP Irp, BOOLEAN FastStart, PDEVICE_OBJECT Fdo)
00469 {
00470     NTSTATUS ntStatus = STATUS_SUCCESS;
00471 
00472     FUNC_ENTER();
00473 
00474     // Are we currently dropping IRPs?
00475     if (!Queue->IsDropping)
00476     {
00477         // No: Process the IRP
00478 
00479         PERF_EVENT_VERBOSE(0x3020, (ULONG)Irp);
00480 
00481         if (FastStart)
00482         {
00483             DBG_ASSERT(Fdo != NULL);
00484 
00485             PERF_EVENT_VERBOSE(0x3021, (ULONG)Irp);
00486 
00487             // The caller has chosen FastStart.
00488             // This means that he wants to IRP to be executed
00489             // immediately (and not be queue) if there is not
00490             // IRP currently being executed in the thread.
00491 
00492             // is there already an IRP in progress?
00493 
00494             if (Queue->CurrentIrp == NULL)
00495             {
00496                 // no, thus, we can do a fast start
00497 
00498                 // Mark this IRP as the current one
00499 
00500                 Queue->CurrentIrp = Irp;
00501 
00502                 // Immediately execute this IRP
00503 
00504                 PERF_EVENT_VERBOSE(0x3022, (ULONG)Irp);
00505 
00506                 ntStatus = Queue->DriverStartIo(Fdo, Irp);
00507 
00508                 PERF_EVENT_VERBOSE(0x3023, 0);
00509             }
00510             else
00511             {
00512                 // There is an IRP being processed. 
00513                 // Unfortunately, this means that a fast 
00514                 // start is not possible.
00515 
00516                 FastStart = FALSE;
00517             }
00518         }
00519 
00520         // Has this IRP already been fast started?
00521 
00522         if (!FastStart)
00523         {
00524             // No: Add the IRP to the Queue
00525 
00526             PERF_EVENT_VERBOSE(0x3024, (ULONG)Irp);
00527 
00528             // First of all, since we will pend the IRP later,
00529             // mark it pending to prevent the race condition
00530 
00531             DBG_IRQL( <= DISPATCH_LEVEL);
00532             IoMarkIrpPending(Irp);
00533 
00534             // Insert the IRP into the queue.
00535 
00536             // no IRQL restrictions apply!
00537             IoCsqInsertIrp(&Queue->IrpQueue, Irp, NULL);
00538 
00539             PERF_EVENT_VERBOSE(0x3025, 0);
00540 
00541             // Wake up the waiting thread which can execute
00542             // the queued IRP
00543 
00544             QueueSignal(Queue);
00545 
00546             PERF_EVENT_VERBOSE(0x3026, 0);
00547 
00548             // Return to the caller that the IRP has been pended.
00549             // This tells the caller that it has to wait until the
00550             // IRP is completed by another means.
00551 
00552             ntStatus = STATUS_PENDING;
00553         }
00554     }
00555     else
00556     {
00557         // We are currently dropping the IRP, thus,
00558         // just return the value set before, and complete the IRP
00559         // without doing anything.
00560 
00561         ntStatus = QueueCompleteIrp(NULL, Irp, Queue->DroppingReturnStatus, 0);
00562     }
00563 
00564     FUNC_LEAVE_NTSTATUS(ntStatus);
00565 }
00566 
00586 static PIRP
00587 QueueRemoveNextIrp(PQUEUE Queue)
00588 {
00589     PIRP irp;
00590 
00591     FUNC_ENTER();
00592 
00593     // Just let the CSQ perform all the necessary steps
00594 
00595     irp = IoCsqRemoveNextIrp(&Queue->IrpQueue, NULL);
00596 
00597     PERF_EVENT_VERBOSE(0x3030, (ULONG)irp);
00598 
00599     FUNC_LEAVE_PTR(irp, PIRP);
00600 }
00601 
00615 VOID
00616 QueueStall(PQUEUE Queue)
00617 {
00618     LONG ret;
00619 
00620     FUNC_ENTER();
00621 
00622     // Increment the number of stall requests
00623 
00624     ret = InterlockedIncrement(&Queue->IsStalled);
00625 
00626     DBG_ASSERT(ret == 0);
00627 
00628     FUNC_LEAVE();
00629 }
00630 
00644 VOID
00645 QueueUnstall(PQUEUE Queue)
00646 {
00647     LONG ret;
00648 
00649     FUNC_ENTER();
00650 
00651     // Decrement the number of stall requests
00652 
00653     ret = InterlockedDecrement(&Queue->IsStalled);
00654 
00655     DBG_ASSERT(ret == 1);
00656 
00657     FUNC_LEAVE();
00658 }
00659 
00660 #if DBG
00661 
00679 BOOLEAN
00680 QueueIsStalled(PQUEUE Queue)
00681 {
00682     BOOLEAN Ret;
00683 
00684     FUNC_ENTER();
00685 
00686     Ret = Queue->IsStalled ? TRUE : FALSE;
00687 
00688     FUNC_LEAVE_BOOLEAN(Ret);
00689 }
00690 
00718 BOOLEAN
00719 QueueIsDropping(PQUEUE Queue)
00720 {
00721     BOOLEAN Ret;
00722 
00723     FUNC_ENTER();
00724 
00725     Ret = Queue->IsDropping ? TRUE : FALSE;
00726 
00727     FUNC_LEAVE_BOOLEAN(Ret);
00728 }
00729 
00730 #endif // #if DBG
00731 
00756 NTSTATUS
00757 QueueCompleteIrp(PQUEUE Queue, PIRP Irp, NTSTATUS NtStatus, ULONG_PTR Information)
00758 {
00759     FUNC_ENTER();
00760 
00761     // If there was a Queue given, the IRP can be the current
00762     // executing IRP of the Queue. Because of this, make sure
00763     // to unset it from executing state ("CurrentIrp") if it
00764     // happens to be the current IRP.
00765 
00766     if (Queue)
00767     {
00768         DBG_ASSERT(Queue->CurrentIrp != NULL);
00769         DBG_ASSERT(Queue->CurrentIrp == Irp);
00770 
00771         if (Queue->CurrentIrp == Irp)
00772         {
00773             Queue->CurrentIrp = NULL;
00774         }
00775     }
00776 
00777     // Set the return values into the IRP
00778 
00779     Irp->IoStatus.Information = Information;
00780     Irp->IoStatus.Status = NtStatus;
00781 
00782     PERF_EVENT_COMPLETEIRP(Irp);
00783 
00784     // Now, complete the IRP.
00785 
00786     DBG_IRQL( <= DISPATCH_LEVEL);
00787     IoCompleteRequest(Irp, IO_NO_INCREMENT);
00788 
00789     // For convenience, return the NTSTATUS which was used to
00790     // complete the IRP.
00791 
00792     FUNC_LEAVE_NTSTATUS(NtStatus);
00793 }
00794 
00816 NTSTATUS
00817 QueuePoll(PQUEUE Queue, PDEVICE_OBJECT Fdo)
00818 {
00819     PIRP irp;
00820 
00821     FUNC_ENTER();
00822 
00823     DBG_IRQL( == PASSIVE_LEVEL);
00824 
00825     DBG_ASSERT(Queue->CurrentIrp == NULL);
00826 
00827     // First, check if there is an IRP in the QUEUE
00828 
00829     PERF_EVENT_VERBOSE(0x3040, 0);
00830 
00831     irp = QueueRemoveNextIrp(Queue);
00832 
00833     PERF_EVENT_VERBOSE(0x3041, (ULONG)irp);
00834 
00835     if (!irp)
00836     {
00837         // There is no IRP in the QUEUE, wait for a new one
00838 
00839         PERF_EVENT_VERBOSE(0x3042, (ULONG)irp);
00840 
00841         KeWaitForSingleObject(&Queue->NotEmptyEvent,
00842             Executive,
00843             KernelMode,
00844             FALSE,
00845             NULL);
00846 
00847         PERF_EVENT_VERBOSE(0x3043, (ULONG)irp);
00848 
00849 #ifdef USE_FAST_START_THREAD
00850 
00851         // Signal to the caller that it can continue
00852 
00853         DBG_IRQL( <= DISPATCH_LEVEL);
00854         KeSetEvent(&Queue->BackSignalEvent, IO_NO_INCREMENT, FALSE);
00855 
00856         PERF_EVENT_VERBOSE(0x3044, (ULONG)irp);
00857 
00858 #endif USE_FAST_START_THREAD
00859 
00860         // Get the next IRP from the QUEUE
00861         // Remember: There might not be any IRP on the
00862         // QUEUE. The caller has to handle this state
00863         // properly.
00864 
00865         irp = QueueRemoveNextIrp(Queue);
00866 
00867         PERF_EVENT_VERBOSE(0x3045, (ULONG)irp);
00868     }
00869 
00870     // If there is an IRP, execute and complete that
00871 
00872     if (irp)
00873     {
00874         DBG_ASSERT(Queue->CurrentIrp == NULL);
00875 
00876         // Set the IRP we just got as the current one
00877 
00878         Queue->CurrentIrp = irp;
00879 
00880         // Execute the IRP by calling DriverStartIo()
00881 
00882 //        KIRQL oldIrql;
00883 //        KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
00884         Queue->DriverStartIo(Fdo, irp);
00885 //        KeLowerIrql(oldIrql);
00886     }
00887 
00888     FUNC_LEAVE_NTSTATUS_CONST(STATUS_SUCCESS);
00889 }
00890 
00900 NTSTATUS
00901 QueueSignal(PQUEUE Queue)
00902 {
00903 #ifdef USE_FAST_START_THREAD
00904 
00905     LARGE_INTEGER timeout;
00906 
00907 #endif // #ifdef USE_FAST_START_THREAD
00908 
00909     FUNC_ENTER();
00910 
00911     // Wake up the waiting thread
00912 
00913 #ifdef USE_FAST_START_THREAD
00914 
00915     // The fast start of the thread is only allowed if we are running
00916     // at PASSIVE_LEVEL.
00917 
00918     PERF_EVENT_VERBOSE(0x3050, 0);
00919 
00920     if (KeGetCurrentIrql() == PASSIVE_LEVEL)
00921     {
00922         // Make sure the "backsignal event" is not set before we start the thread
00923 
00924         DBG_IRQL( <= DISPATCH_LEVEL);
00925         KeClearEvent(&Queue->BackSignalEvent);
00926 
00927         PERF_EVENT_VERBOSE(0x3051, 0);
00928 
00929         // Initialize the timeout value
00930 
00931         timeout.QuadPart = 1;
00932 
00933         // Signal the other thread. Make sure no-one else can disturb us
00934         // by setting the last argument (Wait) to TRUE
00935 
00936         DBG_IRQL( == PASSIVE_LEVEL);
00937         KeSetEvent(&Queue->NotEmptyEvent, IO_NO_INCREMENT, TRUE);
00938 
00939         PERF_EVENT_VERBOSE(0x3052, 0);
00940 
00941         // Make sure the other thread is scheduled by waiting for the
00942         // "back event"
00943 
00944         // This IRQL restriction "normally" applies. Anyway, as the above
00945         // KeSetEvent() was called with Wait == TRUE, in fact, we *are* at
00946         // DISPATCH_LEVEL, so this tests does not make sense.
00947 
00948         // DBG_IRQL( < DISPATCH_LEVEL);
00949         KeWaitForSingleObject(&Queue->BackSignalEvent,
00950             Executive,
00951             KernelMode,
00952             FALSE,
00953             &timeout);
00954 
00955         PERF_EVENT_VERBOSE(0x3053, 0);
00956     }
00957     else
00958     {
00959         // As no fast start is allowed, just signal the thread
00960 
00961         PERF_EVENT_VERBOSE(0x3054, 0);
00962 
00963         DBG_IRQL( <= DISPATCH_LEVEL);
00964         KeSetEvent(&Queue->NotEmptyEvent, IO_NO_INCREMENT, FALSE);
00965 
00966         PERF_EVENT_VERBOSE(0x3055, 0);
00967     }
00968 
00969 #else // #ifdef USE_FAST_START_THREAD
00970 
00971     DBG_IRQL( <= DISPATCH_LEVEL);
00972     KeSetEvent(&Queue->NotEmptyEvent, IO_NO_INCREMENT, FALSE);
00973 
00974 #endif // #ifdef USE_FAST_START_THREAD
00975 
00976 
00977     FUNC_LEAVE_NTSTATUS_CONST(STATUS_SUCCESS);
00978 }
00979 
00998 NTSTATUS
00999 QueueCleanup(PQUEUE Queue, PFILE_OBJECT FileObject)
01000 {
01001     PIRP irp;
01002 
01003     FUNC_ENTER();
01004 
01005     // Check if there is an IRP associated with that FILE_OBJECT
01006 
01007     // No IRQL restrictions apply
01008     irp = IoCsqRemoveNextIrp(&Queue->IrpQueue, FileObject);
01009 
01010     // While there are still IRPs which are associated with the
01011     // given FILE_OBJECT, take them out of the QUEUE and complete them.
01012 
01013     while (irp)
01014     {
01015         QueueCompleteIrp(NULL, irp, STATUS_CANCELLED, 0);
01016 
01017         // Check for next IRP associated with that FILE_OBJECT
01018 
01019         // No IRQL restrictions apply
01020         irp = IoCsqRemoveNextIrp(&Queue->IrpQueue, FileObject);
01021     }
01022 
01024 
01025     FUNC_LEAVE_NTSTATUS_CONST(STATUS_SUCCESS);
01026 }
01027 
01041 BOOLEAN
01042 QueueShouldCancelCurrentIrp(PQUEUE Queue)
01043 {
01044     FUNC_ENTER();
01045 
01046     // Check if the Cancel field of the current IRP has been set
01047 
01048     FUNC_LEAVE_BOOLEAN(Queue->CurrentIrp->Cancel ? TRUE : FALSE);
01049 }

Generated on Sun Apr 30 18:45:58 2006 for opencbm by  doxygen 1.4.2