2020-09-24 20:51:15 +00:00
|
|
|
/*++
|
|
|
|
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
|
|
|
|
FxIrpQueue.cpp
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
|
|
|
|
This module implements a common queue structure for the
|
|
|
|
driver frameworks built around the Cancel Safe Queue model
|
|
|
|
|
|
|
|
Author:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
|
|
|
|
Both kernel and user mode
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include "coreprivshared.hpp"
|
|
|
|
|
|
|
|
// Tracing support
|
|
|
|
extern "C" {
|
2020-10-16 03:30:51 +00:00
|
|
|
// #include "FxIrpQueue.tmh"
|
2020-09-24 20:51:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Public constructors
|
|
|
|
//
|
|
|
|
|
|
|
|
FxIrpQueue::FxIrpQueue(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
InitializeListHead(&m_Queue);
|
|
|
|
|
|
|
|
m_LockObject = NULL;
|
|
|
|
|
|
|
|
m_CancelCallback = NULL;
|
|
|
|
|
|
|
|
m_RequestCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FxIrpQueue::~FxIrpQueue()
|
|
|
|
{
|
|
|
|
ASSERT(IsListEmpty(&m_Queue));
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxIrpQueue::Initialize(
|
|
|
|
__in FxNonPagedObject* LockObject,
|
|
|
|
__in PFN_IRP_QUEUE_CANCEL Callback
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Initialize the FxIrpQueue.
|
|
|
|
|
|
|
|
Set the callback for when an IRP gets cancelled.
|
|
|
|
|
|
|
|
The callback function is only called when an IRP
|
|
|
|
gets cancelled, and is called with no locks held.
|
|
|
|
|
|
|
|
The cancel function that the caller registers is
|
|
|
|
responsible for completing the IRP with IoCompleteRequest.
|
|
|
|
|
|
|
|
If no Cancel Callback is set, or is set to NULL, IRP's will
|
|
|
|
be automatically completed with STATUS_CANCELED by
|
|
|
|
the FxIrpQueue when they are canceled.
|
|
|
|
|
|
|
|
|
|
|
|
If the caller supplies a LockObject, this object is used
|
|
|
|
to synchronize cancellation access to the list, but it is
|
|
|
|
expected that the caller be holding the same lock for insert/remove
|
|
|
|
operations. This allows the caller to perform insert/remove operations
|
|
|
|
using its own lock in a race free manner.
|
|
|
|
|
|
|
|
If a LockObject is not supplied, the FxIrpQueue uses its own lock.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
LockObject - Object whose lock controls the queue
|
|
|
|
|
|
|
|
Callback - Driver callback function
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
ASSERT(LockObject != NULL);
|
|
|
|
|
|
|
|
m_CancelCallback = Callback;
|
|
|
|
m_LockObject = LockObject;
|
|
|
|
}
|
|
|
|
|
2021-06-11 12:29:21 +00:00
|
|
|
|
2020-09-24 20:51:15 +00:00
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxIrpQueue::InsertTailRequest(
|
|
|
|
__inout MdIrp Irp,
|
|
|
|
__in_opt PMdIoCsqIrpContext Context,
|
|
|
|
__out_opt ULONG* pRequestCount
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Enqueue a request to the end of the queue (FIFO) and
|
|
|
|
marks it as pending.
|
|
|
|
|
|
|
|
The PMdIoCsqIrpContext is associated with the IRP.
|
|
|
|
|
|
|
|
PIO_CSQ_IRP_CONTEXT must be in non-paged pool, and can
|
|
|
|
not be released until the IRP is is finally released from
|
|
|
|
the queue.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Irp - Pointer to IRP
|
|
|
|
|
|
|
|
Context - Pointer to caller allocated CSQ context
|
|
|
|
|
|
|
|
pRequestCount - Location to return new request count of queue
|
|
|
|
after insertion
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
STATUS_SUCCESS - Operation completed.
|
|
|
|
|
|
|
|
STATUS_CANCELLED - Request was cancelled, and not inserted
|
|
|
|
Call is responsible for completing it.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
// Note: This marks the IRP Pending
|
|
|
|
Status = InsertIrpInQueue(
|
|
|
|
Irp, // Irp to insert
|
|
|
|
Context, // PIO_CSQ_IRP_CONTEXT
|
|
|
|
FALSE, // InsertInHead
|
|
|
|
pRequestCount
|
|
|
|
);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2021-06-11 12:29:21 +00:00
|
|
|
|
2020-09-24 20:51:15 +00:00
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxIrpQueue::InsertHeadRequest(
|
|
|
|
__inout MdIrp Irp,
|
|
|
|
__in_opt PMdIoCsqIrpContext Context,
|
|
|
|
__out_opt ULONG* pRequestCount
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Enqueue a request to the head of the queue and
|
|
|
|
marks it as pending.
|
|
|
|
|
|
|
|
The PIO_CSQ_IRP_CONTEXT is associated with the IRP.
|
|
|
|
|
|
|
|
PIO_CSQ_IRP_CONTEXT must be in non-paged pool, and can
|
|
|
|
not be released until the IRP is is finally released from
|
|
|
|
the queue.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Irp - Pointer to IRP
|
|
|
|
|
|
|
|
Context - Pointer to caller allocated CSQ context
|
|
|
|
|
|
|
|
pRequestCount - Location to return new request count of queue
|
|
|
|
after insertion
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
STATUS_SUCCESS - Operation completed.
|
|
|
|
|
|
|
|
STATUS_CANCELLED - Request was cancelled, and not inserted
|
|
|
|
Call is responsible for completing it.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
// Note: This marks the IRP Pending
|
|
|
|
Status = InsertIrpInQueue(
|
|
|
|
Irp, // Irp to insert
|
|
|
|
Context, // PIO_CSQ_IRP_CONTEXT
|
|
|
|
TRUE, // InsertInHead
|
|
|
|
pRequestCount
|
|
|
|
);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2021-06-11 12:29:21 +00:00
|
|
|
|
2020-09-24 20:51:15 +00:00
|
|
|
MdIrp
|
|
|
|
FxIrpQueue::GetNextRequest(
|
|
|
|
__out PMdIoCsqIrpContext* pCsqContext
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Returns an IRP from the queue, and if successful
|
|
|
|
the IRP is no longer on the CSQ (m_Queue) and
|
|
|
|
is not non-cancellable.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
return RemoveNextIrpFromQueue(NULL, pCsqContext);
|
|
|
|
}
|
|
|
|
|
2021-06-11 12:29:21 +00:00
|
|
|
|
2020-09-24 20:51:15 +00:00
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxIrpQueue::GetNextRequest(
|
|
|
|
__in_opt PMdIoCsqIrpContext TagContext,
|
|
|
|
__in_opt MdFileObject FileObject,
|
|
|
|
__out FxRequest** ppOutRequest
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Returns a request from the queue using an optional
|
|
|
|
FileObject or TagContext.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
MdIrp Irp;
|
|
|
|
FxRequest* pRequest;
|
|
|
|
PMdIoCsqIrpContext pCsqContext;
|
|
|
|
|
|
|
|
if( TagContext == NULL ) {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns an IRP from the queue, and if successful
|
|
|
|
// the IRP is no longer on the CSQ (m_Queue) and
|
|
|
|
// is not non-cancellable.
|
|
|
|
//
|
|
|
|
Irp = RemoveNextIrpFromQueue(
|
|
|
|
FileObject, // PeekContext
|
|
|
|
&pCsqContext
|
|
|
|
);
|
|
|
|
|
|
|
|
if( Irp != NULL ) {
|
|
|
|
|
|
|
|
pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
|
|
|
|
|
|
|
|
*ppOutRequest = pRequest;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Handle TagRequest Case
|
|
|
|
Irp = RemoveIrpFromQueueByContext(
|
|
|
|
TagContext
|
|
|
|
);
|
|
|
|
|
|
|
|
if( Irp != NULL ) {
|
|
|
|
pRequest = FxRequest::RetrieveFromCsqContext(TagContext);
|
|
|
|
|
|
|
|
*ppOutRequest = pRequest;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxIrpQueue::PeekRequest(
|
|
|
|
__in_opt PMdIoCsqIrpContext TagContext,
|
|
|
|
__in_opt MdFileObject FileObject,
|
|
|
|
__out FxRequest** ppOutRequest
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
PeekRequest allows a caller to enumerate through requests in
|
|
|
|
a queue, optionally only returning requests that match a specified
|
|
|
|
FileObject.
|
|
|
|
|
|
|
|
The first call specifies TagContext == NULL, and the first request
|
|
|
|
in the queue that matches the FileObject is returned.
|
|
|
|
|
|
|
|
Subsequent requests specify the previous request value as the
|
|
|
|
TagContext, and searching will continue at the request that follows.
|
|
|
|
|
|
|
|
If the queue is empty, there are no requests after TagContext, or no
|
|
|
|
requests match the FileObject, NULL is returned.
|
|
|
|
|
|
|
|
If FileObject == NULL, this matches any FileObject in a request.
|
|
|
|
|
|
|
|
If a WDF_REQUEST_PARAMETERS structure is supplied, the information
|
|
|
|
from the request is returned to allow the driver to further examine
|
|
|
|
the request to decide whether to service it.
|
|
|
|
|
|
|
|
If a TagRequest is specified, and it is not found, the return
|
|
|
|
status STATUS_NOT_FOUND means that the queue should
|
|
|
|
be re-scanned. This is because the TagRequest was cancelled from
|
|
|
|
the queue, or if the queue was active, delivered to the driver.
|
|
|
|
There may still be un-examined requests on the queue that match
|
|
|
|
the drivers search criteria, but the search marker has been lost.
|
|
|
|
|
|
|
|
Re-scanning the queue starting with TagRequest == NULL and
|
|
|
|
continuing until STATUS_NO_MORE_ENTRIES is returned will ensure
|
|
|
|
all requests have been examined.
|
|
|
|
|
|
|
|
Enumerating an active queue with this API could result in the
|
|
|
|
driver frequently having to re-scan.
|
|
|
|
|
|
|
|
If a successful return of a Request object handle occurs, the driver
|
|
|
|
*must* call WdfObjectDereference when done with it.
|
|
|
|
|
|
|
|
NOTE: Synchronization Details
|
|
|
|
|
|
|
|
The driver is allowed to "peek" at requests that are still on
|
|
|
|
the Cancel Safe Queue without removing them. This means that
|
|
|
|
the peek context value used (TagRequest) could go away while
|
|
|
|
still holding it. This does not seem bad in itself, but the request
|
|
|
|
could immediately be re-used by a look aside list and be re-submitted
|
|
|
|
to the queue. At this point, the "tag" value means a completely different
|
|
|
|
request.
|
|
|
|
|
|
|
|
This race is dealt with by reference counting the FxRequest object
|
|
|
|
that contains our PIO_CSQ_IRP_CONTEXT, so its memory remains valid
|
|
|
|
after a cancel, and the driver explicitly releases it with
|
|
|
|
WdfObjectDereference.
|
|
|
|
|
|
|
|
But if this reference is not added under the CSQ's spinlock, there
|
|
|
|
could be a race in which the I/O gets cancelled and the cancel
|
|
|
|
callback completes the request, before we add our reference count.
|
|
|
|
This would then result in attempting to reference count invalid
|
|
|
|
memory. So to close this race, this routine returns the referenced
|
|
|
|
FxRequest object as its result.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
TagRequest - If !NULL, request to begin search at
|
|
|
|
|
|
|
|
FileObject - If !NULL, FileObject to match in the request
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
STATUS_NOT_FOUND - TagContext was specified, but not
|
|
|
|
found in the queue. This could be
|
|
|
|
because the request was cancelled,
|
|
|
|
or is part of an active queue and
|
|
|
|
the request was passed to the driver
|
|
|
|
or forwarded to another queue.
|
|
|
|
|
|
|
|
STATUS_NO_MORE_ENTRYS - The queue is empty, or no more requests
|
|
|
|
match the selection criteria of TagRequest
|
|
|
|
and FileObject specified above.
|
|
|
|
|
|
|
|
STATUS_SUCCESS - A request context was returned in
|
|
|
|
pOutRequest.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
PLIST_ENTRY nextEntry;
|
|
|
|
FxIrp nextIrp(NULL);
|
|
|
|
PMdIoCsqIrpContext pCsqContext;
|
|
|
|
BOOLEAN FoundTag = (TagContext == NULL) ? TRUE : FALSE;
|
|
|
|
FxRequest* pRequest;
|
|
|
|
|
|
|
|
for( nextEntry = m_Queue.Flink; nextEntry != &this->m_Queue; nextEntry = nextEntry->Flink) {
|
|
|
|
|
|
|
|
nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
|
|
|
|
|
|
|
|
if(nextIrp.IsCanceled()) {
|
|
|
|
//
|
|
|
|
// This IRP is cancelled and the WdmCancelRoutine is about to run or waiting
|
|
|
|
// for us to drop the lock. So skip this one.
|
|
|
|
//
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pCsqContext = (PMdIoCsqIrpContext)nextIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
|
|
|
|
|
|
|
|
if( FoundTag ) {
|
|
|
|
|
|
|
|
if( FileObject != NULL ) {
|
|
|
|
|
|
|
|
if(nextIrp.GetFileObject() == FileObject ) {
|
|
|
|
|
|
|
|
pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Must add the reference here under the protection
|
|
|
|
// of the cancel safe queues spinlock
|
|
|
|
//
|
|
|
|
pRequest->ADDREF(NULL);
|
|
|
|
|
|
|
|
*ppOutRequest = pRequest;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Must add the reference here under the protection
|
|
|
|
// of the cancel safe queues spinlock
|
|
|
|
//
|
|
|
|
pRequest->ADDREF(NULL);
|
|
|
|
|
|
|
|
*ppOutRequest = pRequest;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// If we found the tag, we want the *next* entry
|
|
|
|
if( pCsqContext == TagContext ) {
|
|
|
|
FoundTag = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// If the caller supplied a tag, and it was
|
|
|
|
// not found, return a different code since
|
|
|
|
// the caller needs to re-scan the queue.
|
|
|
|
//
|
|
|
|
if( (TagContext != NULL) && !FoundTag ) {
|
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MdIrp
|
|
|
|
FxIrpQueue::RemoveRequest(
|
|
|
|
__in PMdIoCsqIrpContext Context
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Returns a request from the queue.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
MdIrp Irp;
|
|
|
|
|
|
|
|
ASSERT(Context != NULL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns an IRP from the queue, and if success
|
|
|
|
// the IRP is no longer on the CSQ (m_Queue) and
|
|
|
|
// is not non-cancellable.
|
|
|
|
//
|
|
|
|
Irp = RemoveIrpFromQueueByContext(Context);
|
|
|
|
|
|
|
|
return Irp;
|
|
|
|
}
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxIrpQueue::InsertIrpInQueue(
|
|
|
|
__inout MdIrp Irp,
|
|
|
|
__in_opt PMdIoCsqIrpContext Context,
|
|
|
|
__in BOOLEAN InsertInHead,
|
|
|
|
__out_opt ULONG* pRequestCount
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Insert the IRP in the queue. If the IRP is already cancelled
|
|
|
|
it removes the IRP from the queue and returns STATUS_CANCELLED.
|
|
|
|
|
|
|
|
If the IRP is cancelled and the CancelRoutine has already started
|
|
|
|
execution, then this function returns STATUS_SUCCESS.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Irp - Pointer to IRP
|
|
|
|
|
|
|
|
Context - Pointer to caller allocated CSQ context
|
|
|
|
|
|
|
|
InsertInHead - TRUE for head, FALSE for tail.
|
|
|
|
|
|
|
|
pRequestCount - Location to return new request count of queue
|
|
|
|
after insertion
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
STATUS_SUCCESS - Operation completed.
|
|
|
|
|
|
|
|
STATUS_CANCELLED - if the request is already cancelled.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
FxIrp irp(Irp);
|
|
|
|
MdCancelRoutine cancelRoutine;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Set the association between the context and the IRP.
|
|
|
|
//
|
|
|
|
|
|
|
|
if (Context) {
|
|
|
|
irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, Context);
|
|
|
|
Context->Irp = Irp;
|
|
|
|
Context->Csq = (PIO_CSQ)this;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Context->Type = FX_IRP_QUEUE_ENTRY_IDENTIFIER;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Currently always require context, but this will change when we
|
|
|
|
// allow queuing or low level IRP's without FxRequest headers allocated
|
|
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
|
|
|
|
|
|
irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if it's a head insertion
|
|
|
|
if( InsertInHead ) {
|
|
|
|
InsertHeadList(
|
|
|
|
&m_Queue,
|
|
|
|
irp.ListEntry()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
InsertTailList(
|
|
|
|
&m_Queue,
|
|
|
|
irp.ListEntry()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_RequestCount++;
|
|
|
|
|
|
|
|
if( pRequestCount != NULL ) {
|
|
|
|
*pRequestCount = m_RequestCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
irp.MarkIrpPending();
|
|
|
|
|
|
|
|
cancelRoutine = irp.SetCancelRoutine(_WdmCancelRoutineInternal);
|
|
|
|
|
|
|
|
ASSERT(!cancelRoutine);
|
|
|
|
UNREFERENCED_PARAMETER(cancelRoutine);
|
|
|
|
|
|
|
|
if (irp.IsCanceled()) {
|
|
|
|
|
|
|
|
cancelRoutine = irp.SetCancelRoutine(NULL);
|
|
|
|
|
|
|
|
if (cancelRoutine) {
|
|
|
|
|
|
|
|
// Remove the IRP from the list
|
|
|
|
RemoveIrpFromListEntry(&irp);
|
|
|
|
|
|
|
|
if (Context) {
|
|
|
|
Context->Irp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Caller does not want us to recurse on their lock by invoking
|
|
|
|
// the m_CancelCallback on the insert path. So they will complete
|
|
|
|
// the IRP themselves.
|
|
|
|
//
|
|
|
|
return STATUS_CANCELLED;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
//
|
|
|
|
// The cancel routine beat us to it.
|
|
|
|
//
|
|
|
|
DO_NOTHING();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FX_VF_METHOD(FxIrpQueue, VerifyRemoveIrpFromQueueByContext)(
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
|
|
__in PMdIoCsqIrpContext Context
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Makes sure that the specified Request (context) belongs to this IRP queue.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAGED_CODE_LOCKED();
|
|
|
|
|
|
|
|
if (FxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
|
|
|
if (Context->Irp != NULL &&
|
|
|
|
(Context->Type != FX_IRP_QUEUE_ENTRY_IDENTIFIER ||
|
|
|
|
Context->Csq != (PIO_CSQ)this)) {
|
|
|
|
|
|
|
|
//
|
|
|
|
// This should never happen. Bugcheck before corrupting memory.
|
|
|
|
//
|
|
|
|
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGREQUEST,
|
|
|
|
"Irp 0x%p (Context 0x%p) not on IRP queue 0x%p\n",
|
|
|
|
Context->Irp, Context, this);
|
|
|
|
|
|
|
|
FxVerifierBugCheck(FxDriverGlobals,
|
|
|
|
WDF_REQUEST_FATAL_ERROR,
|
|
|
|
WDF_REQUEST_FATAL_ERROR_REQUEST_NOT_IN_QUEUE,
|
|
|
|
(ULONG_PTR) Context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MdIrp
|
|
|
|
FxIrpQueue::RemoveIrpFromQueueByContext(
|
|
|
|
__in PMdIoCsqIrpContext Context
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Using the context it remove the associated IRP from the queue.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
MdIrp irp;
|
|
|
|
MdCancelRoutine cancelRoutine;
|
|
|
|
|
|
|
|
if (Context->Irp ) {
|
|
|
|
//
|
|
|
|
// Make sure the Irp belongs to this queue.
|
|
|
|
//
|
|
|
|
ASSERT(Context->Csq == (PIO_CSQ)this);
|
|
|
|
VerifyRemoveIrpFromQueueByContext(m_LockObject->GetDriverGlobals(),
|
|
|
|
Context);
|
|
|
|
|
|
|
|
irp = Context->Irp;
|
|
|
|
|
|
|
|
FxIrp fxIrp(irp);
|
|
|
|
|
|
|
|
cancelRoutine = fxIrp.SetCancelRoutine(NULL);
|
|
|
|
if (!cancelRoutine) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(Context == fxIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY));
|
|
|
|
|
|
|
|
RemoveIrpFromListEntry(&fxIrp);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Break the association.
|
|
|
|
//
|
|
|
|
|
|
|
|
Context->Irp = NULL;
|
|
|
|
fxIrp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
|
|
|
|
|
|
|
|
ASSERT(Context->Csq == (PIO_CSQ)this);
|
|
|
|
|
|
|
|
return irp;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MdIrp
|
|
|
|
FxIrpQueue::PeekNextIrpFromQueue(
|
|
|
|
__in_opt MdIrp Irp,
|
|
|
|
__in_opt PVOID PeekContext
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
This API look up IRP's in the queue.
|
|
|
|
|
|
|
|
If Irp == NULL, it returns one from the head
|
|
|
|
of the queue.
|
|
|
|
|
|
|
|
If Irp != NULL, it is a "peek context", and the
|
|
|
|
routine returns the *next* IRP in the queue.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLIST_ENTRY nextEntry;
|
|
|
|
PLIST_ENTRY listHead;
|
|
|
|
FxIrp irp(Irp);
|
|
|
|
FxIrp nextIrp(NULL);
|
|
|
|
|
|
|
|
listHead = &m_Queue;
|
|
|
|
|
|
|
|
//
|
|
|
|
// If the IRP is NULL, we will start peeking from the listhead, else
|
|
|
|
// we will start from that IRP onwards. This is done under the
|
|
|
|
// assumption that new IRPs are always inserted at the tail.
|
|
|
|
//
|
|
|
|
|
|
|
|
if(Irp == NULL) {
|
|
|
|
nextEntry = listHead->Flink;
|
|
|
|
} else {
|
|
|
|
nextEntry = irp.ListEntry()->Flink;
|
|
|
|
}
|
|
|
|
|
|
|
|
while(nextEntry != listHead) {
|
|
|
|
|
|
|
|
nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
|
|
|
|
|
|
|
|
//
|
|
|
|
// If PeekContext is supplied, it's a search for an IRP associated
|
|
|
|
// with a particular file object.
|
|
|
|
//
|
|
|
|
if(PeekContext) {
|
|
|
|
|
|
|
|
if(nextIrp.GetFileObject() == (MdFileObject) PeekContext) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
nextIrp.SetIrp(NULL);
|
|
|
|
|
|
|
|
nextEntry = nextEntry->Flink;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nextIrp.GetIrp();
|
|
|
|
}
|
|
|
|
|
|
|
|
MdIrp
|
|
|
|
FxIrpQueue::RemoveNextIrpFromQueue(
|
|
|
|
__in_opt PVOID PeekContext,
|
|
|
|
__out_opt PMdIoCsqIrpContext* pCsqContext
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
This routine will return a pointer to the next IRP in the queue adjacent to
|
|
|
|
the irp passed as a parameter. If the irp is NULL, it returns the IRP at the head of
|
|
|
|
the queue.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PMdIoCsqIrpContext context;
|
|
|
|
MdCancelRoutine cancelRoutine;
|
|
|
|
FxIrp fxIrp(NULL);
|
|
|
|
|
|
|
|
fxIrp.SetIrp(PeekNextIrpFromQueue(NULL, PeekContext));
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
if (!fxIrp.GetIrp()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelRoutine = fxIrp.SetCancelRoutine(NULL);
|
|
|
|
if (!cancelRoutine) {
|
|
|
|
fxIrp.SetIrp(PeekNextIrpFromQueue(fxIrp.GetIrp(), PeekContext));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
RemoveIrpFromListEntry(&fxIrp); // Remove this IRP from the queue
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
context = (PMdIoCsqIrpContext)fxIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
|
|
|
|
if (context->Type == FX_IRP_QUEUE_ENTRY_IDENTIFIER) {
|
|
|
|
context->Irp = NULL;
|
|
|
|
ASSERT(context->Csq == (PIO_CSQ)this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pCsqContext != NULL) {
|
|
|
|
*pCsqContext = context;
|
|
|
|
}
|
|
|
|
|
|
|
|
fxIrp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
|
|
|
|
|
|
|
|
return fxIrp.GetIrp();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxIrpQueue::_WdmCancelRoutineInternal(
|
|
|
|
__inout MdDeviceObject DeviceObject,
|
|
|
|
__in __drv_useCancelIRQL MdIrp Irp
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
This is the function called by WDM on the IRP when a cancel occurs
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PMdIoCsqIrpContext irpContext;
|
|
|
|
FxIrpQueue* p;
|
|
|
|
KIRQL irql;
|
|
|
|
FxIrp irp(Irp);
|
|
|
|
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
|
|
|
|
Mx::ReleaseCancelSpinLock(irp.GetCancelIrql());
|
|
|
|
|
|
|
|
irpContext = (PMdIoCsqIrpContext)irp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Decide if we have a PIO_CSQ_IRP_CONTEXT or an FxIrpQueue*
|
|
|
|
//
|
|
|
|
if (irpContext->Type == FX_IRP_QUEUE_ENTRY_IDENTIFIER) {
|
|
|
|
p = (FxIrpQueue*)irpContext->Csq;
|
|
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
|
|
p = (FxIrpQueue*)irpContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(p);
|
|
|
|
|
|
|
|
p->LockFromCancel(&irql);
|
|
|
|
|
|
|
|
// Remove the IRP from the list
|
|
|
|
p->RemoveIrpFromListEntry(&irp);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Break the association if necessary.
|
|
|
|
//
|
|
|
|
|
|
|
|
if (irpContext != (PMdIoCsqIrpContext)p) {
|
|
|
|
irpContext->Irp = NULL;
|
|
|
|
|
|
|
|
irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// We are calling cancel-callback of the owning object with the lock
|
|
|
|
// held so that it can successfully deliver the canceled request to the driver
|
|
|
|
// if needed. If we don't hold the lock, we run into a race condition between
|
|
|
|
// thread that's deleting the queue and this routine that's trying to deliver
|
|
|
|
// a request to the queue being deleted. So the way it happens is that the dispose
|
|
|
|
// call of queue waits for the request count to go zero. When we remove the
|
|
|
|
// last Irp from the list above, we end up dropping the count to zero. This causes
|
|
|
|
// the delete thread to run thru and destroy the FxIoQueue object. So to avoid that
|
|
|
|
// after popping the request from the FxIrpQueue, we have to call into the FxIoQueue
|
|
|
|
// with the lock and insert the request back into the FxIoQueue list so that delete
|
|
|
|
// thread will wait until the request is delivered to the driver.
|
|
|
|
//
|
|
|
|
if( p->m_CancelCallback != NULL ) {
|
|
|
|
p->m_CancelCallback(p, Irp, irpContext, irql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
p->UnlockFromCancel(irql);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Dispose of the IRP ourselves
|
|
|
|
//
|
|
|
|
irp.SetStatus(STATUS_CANCELLED);
|
|
|
|
irp.SetInformation(0);
|
|
|
|
|
|
|
|
DoTraceLevelMessage(p->m_LockObject->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
|
|
|
|
"Irp 0x%p on Queue 0x%p Cancelled\n", Irp, p);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Breakpoint for now. This usually means that someone
|
|
|
|
// is going to leak some driver frameworks state...
|
|
|
|
//
|
|
|
|
FxVerifierDbgBreakPoint(p->m_LockObject->GetDriverGlobals());
|
|
|
|
|
|
|
|
irp.CompleteRequest(IO_NO_INCREMENT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FxIrpQueue::IsIrpInQueue(
|
|
|
|
__in PMdIoCsqIrpContext Context
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the list to see if any of the IRPs there has
|
|
|
|
a context that matches the input one.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLIST_ENTRY nextEntry;
|
|
|
|
FxIrp nextIrp(NULL);
|
|
|
|
PMdIoCsqIrpContext pCsqContext;
|
|
|
|
|
|
|
|
nextEntry = m_Queue.Flink;
|
|
|
|
|
|
|
|
while( nextEntry != &m_Queue ) {
|
|
|
|
nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
|
|
|
|
|
|
|
|
pCsqContext = (PMdIoCsqIrpContext)nextIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
|
|
|
|
|
|
|
|
if( pCsqContext == Context ) {
|
|
|
|
ASSERT(Context->Irp == nextIrp.GetIrp());
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nextEntry = nextEntry->Flink;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|