FltDoCompletionProcessingWhenSafe & IRP_MJ_WRITE

Hello,

I am in bit of a trouble when I am using FltDoCompletionProcessingWhenSafe with IRP_MJ_WRITE. What I am doing is writing a simple logging driver which monitors all read & write activities. I log necessary things in post-operation calback. Because postoperation callback can be called at DPC level, many kernel routines are not available here, so as per the documentation I call FltDoCompletionProcessingWhenSafe for doing things which I can’t do in postoperation callback. But here my driver simply crashes with STATUS_ACCESS_VIOLATION code. Here’s the code,

FLT_POSTOP_CALLBACK_STATUS
LogPostOperationCallback(
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{
BOOLEAN bRet;
FLT_POSTOP_CALLBACK_STATUS RetPostOperationStatus;

if (FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING))
{
DbgPrint(“Draining bit set.”);
return FLT_POSTOP_FINISHED_PROCESSING;
}

bRet = FLT_IS_IRP_OPERATION(Data);
if (FALSE == bRet)
{
DbgPrint(“NOT IRP based…”);
return FLT_POSTOP_FINISHED_PROCESSING;
}

if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO))
{
DbgPrint(“Paging IO…”);
return FLT_POSTOP_FINISHED_PROCESSING;
}

bRet = FltDoCompletionProcessingWhenSafe(
Data,
FltObjects,
CompletionContext,
Flags,
SafePostCallback,
&RetPostOperationStatus
);
if (FALSE == bRet && FLT_POSTOP_FINISHED_PROCESSING == RetPostOperationStatus)
{
DbgPrint(“FltDoCompletionProcessingWhenSafe failed.”);
return FLT_POSTOP_FINISHED_PROCESSING;
}

return FLT_POSTOP_FINISHED_PROCESSING;
}
FLT_POSTOP_CALLBACK_STATUS
SafePostCallback(
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{
DbgPrint(“IN safepostcallback…”);
return FLT_POSTOP_FINISHED_PROCESSING;
}

Does this mean I should not use FltDoCompletionProcessingWhenSafe with IRP_MJ_WRITE? The same code works perfectly fine for IRP_MJ_READ.

Hi!

You should return from PostOp with …

return RetPostOperationStatus;

because this probably contains FLT_POSTOP_MORE_PROCESSING_REQUIRED

wrote news:xxxxx@ntfsd…
> Hello,
>
> I am in bit of a trouble when I am using FltDoCompletionProcessingWhenSafe
> with IRP_MJ_WRITE. What I am doing is writing a simple logging driver
> which monitors all read & write activities. I log necessary things in
> post-operation calback. Because postoperation callback can be called at
> DPC level, many kernel routines are not available here, so as per the
> documentation I call FltDoCompletionProcessingWhenSafe for doing things
> which I can’t do in postoperation callback. But here my driver simply
> crashes with STATUS_ACCESS_VIOLATION code. Here’s the code,
>
> FLT_POSTOP_CALLBACK_STATUS
> LogPostOperationCallback(
> inout PFLT_CALLBACK_DATA Data,
>
in PCFLT_RELATED_OBJECTS FltObjects,
> in PVOID CompletionContext,
>
in FLT_POST_OPERATION_FLAGS Flags
> )
> {
> BOOLEAN bRet;
> FLT_POSTOP_CALLBACK_STATUS RetPostOperationStatus;
>
> if (FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING))
> {
> DbgPrint(“Draining bit set.”);
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> bRet = FLT_IS_IRP_OPERATION(Data);
> if (FALSE == bRet)
> {
> DbgPrint(“NOT IRP based…”);
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO))
> {
> DbgPrint(“Paging IO…”);
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> bRet = FltDoCompletionProcessingWhenSafe(
> Data,
> FltObjects,
> CompletionContext,
> Flags,
> SafePostCallback,
> &RetPostOperationStatus
> );
> if (FALSE == bRet && FLT_POSTOP_FINISHED_PROCESSING ==
> RetPostOperationStatus)
> {
> DbgPrint(“FltDoCompletionProcessingWhenSafe failed.”);
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
> FLT_POSTOP_CALLBACK_STATUS
> SafePostCallback(
> inout PFLT_CALLBACK_DATA Data,
>
in PCFLT_RELATED_OBJECTS FltObjects,
> in PVOID CompletionContext,
>
in FLT_POST_OPERATION_FLAGS Flags
> )
> {
> DbgPrint(“IN safepostcallback…”);
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> Does this mean I should not use FltDoCompletionProcessingWhenSafe with
> IRP_MJ_WRITE? The same code works perfectly fine for IRP_MJ_READ.
>

Hello Frank,

Thank you vary much, it indeed solved my problem.
Still I am not getting exactly why this STATUS_ACCESS_VIOLATION occured?
When I returned FLT_POSTOP_FINISHED_PROCESSING from my minifilter, filter manager didn’t halt its completion processing and carried on instead, where as later in some point of time some arbitrary thread will execute my safepostcallback.
I am getting exactly what must be happening?
Could please explain this more technically?

In the underlying system, there is an IRP. If you return FLT_POSTOP_MORE_PROCESSING_REQUIRED, it tells the I/O Manager that you need to keep the IRP around and will complete it later. If you return FLT_POSTOP_FINISHED_PROCESSING you tell the I/O Manager that you’re done with the IRP and it can be deleted.

In the meantime, FltDoCompletionProcessingWhenSafe has queued a work item to call you back in a worker thread context. It relies upon that IRP being present (and it doesn’t know what you will return from your own function so it’s telling you what IT has done.) By ignoring that return code, you cause the original IRP to be freed so that when the work item runs the IRP isn’t valid any longer.

This is a variation of the subject I first discussed more than 10 years ago about I/O completion (see http://www.osronline.com/article.cfm?id=83).

Tony
OSR

Hope to see everyone September 19 for the next Developing File Systems for Windows seminar (http://www.osr.com/fsd.html)

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Monday, August 22, 2011 5:29 AM
To: ntfsd redirect
Subject: RE:[ntfsd] FltDoCompletionProcessingWhenSafe & IRP_MJ_WRITE

Hello Frank,

Thank you vary much, it indeed solved my problem.
Still I am not getting exactly why this STATUS_ACCESS_VIOLATION occured?
When I returned FLT_POSTOP_FINISHED_PROCESSING from my minifilter, filter manager didn’t halt its completion processing and carried on instead, where as later in some point of time some arbitrary thread will execute my safepostcallback.
I am getting exactly what must be happening?
Could please explain this more technically?


NTFSD is sponsored by OSR

For our schedule of debugging and file system seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Thanks Tony for explaining the internals, I read your article but could understand very few thing as I am just beginner in windows device driver development. Still I have one question in my mind, should I call FltCompletePendedPostOperation() from my safepostcallback? The documentation of DDK states that “When the operation is eventually dequeued and processed, the minifilter driver can call FltCompletePendedPostOperation to return control of the operation to the Filter Manager, which then resumes completion processing.” or it gets called in the following manner:
workerthread()
{
// Does some work.
//Call my safepostcallback function.
safepostcallback();
//Call FltCompletePendedPostOperation() to tell Filter manager to resume its I/O completion processing.
}

I think it must be happening in the above manner, since as per the doc FltCompletePendedPostOperation must be called so Filter managers I/O completion resumes & as of now I not calling it from my safepostcallback function.
Please correct if I am going wrong, it will help me to get my concepts right.

No, you don’t necessarily need to call FltCompletePendedPostOperation() from
your callback in all cases. You only need to call it if you actually return
FLT_POSTOP_MORE_PROCESSING_REQUIRED.

So the logic in FltMgr’s worker thread is more along the lines of:
Workerthread(){
status = safepostcallback();
if (status == FLT_POSTOP_FINISHED_PROCESSING) {
FltCompletePendedPostOperation();
} else {
// this means the filter pended the operation and will complete it later or
has already
// completed it so FltMgr can’t do anything since the request may already be
completed
}
}

Does this make sense ?

Thanks,
Alex.

Thank you very much alex for providing your views.
I don’t fully agree with your statement of “You only need to call it if you actually return FLT_POSTOP_MORE_PROCESSING_REQUIRED.”
What I think is, calling FltCompletePendedPostOperation() is solely a responsibility of worker thread.
So in my safepostcallback function there will never be a statement like this:
FltCompletePendedPostOperation();

Correct me if I am thinking in wrong direction.

Alex meant: “You only need to call it if you actually return
FLT_POSTOP_MORE_PROCESSING_REQUIRED … from your SafePostCallback”. This
will keep the completion processing deferred and from this point on it’s
your responsibility to call FltCompletePendedPostOperation() when you’re
ready to let the operation go.

By the way: the “swapBuffers” sample is a good sample to check out how
FltDoCompletionProcessingWhenSafe should be applied.

wrote news:xxxxx@ntfsd…
> Thank you very much alex for providing your views.
> I don’t fully agree with your statement of “You only need to call it if
> you actually return FLT_POSTOP_MORE_PROCESSING_REQUIRED.”
> What I think is, calling FltCompletePendedPostOperation() is solely a
> responsibility of worker thread.
> So in my safepostcallback function there will never be a statement like
> this:
> FltCompletePendedPostOperation();
>
> Correct me if I am thinking in wrong direction.
>
>

Ok, but from where I should give a call to FltCompletePendedPostOperation()? From safepostcallback()??? But i have already returned FLT_POSTOP_MORE_PROCESSING_REQUIRED from here.

> … From safepostcallback()??? But i have already returned

FLT_POSTOP_MORE_PROCESSING_REQUIRED from here.

Nope, you haven’t. You’ve returned FLT_POSTOP_MORE_PROCESSING_REQUIRED from
your *PostOp*. From your *SafePostOp* you usually return
FLT_POSTOP_FINISHED_PROCESSING.

Yes so it eventually means that *safepostcallback* in all probability will return FLT_POSTOP_FINISHED_PROCESSING & from worker thread FltCompletePendedPostOperation() will be called and I’ll not have to give it a call manually? M i right?

This is exactly what Alex pseudo code snippet tried to tell you. And he
should know … because Alex has insight to Filter Manager sources.

wrote news:xxxxx@ntfsd…
> Yes so it eventually means that safepostcallback in all probability will
> return FLT_POSTOP_FINISHED_PROCESSING & from worker thread
> FltCompletePendedPostOperation() will be called and I’ll not have to give
> it a call manually? M i right?
>

Ok, thanks frank for the help.

Frank is right about everything except one thing :
“Alex has insight to Filter Manager sources” . That’s not the case anymore

But I’m pretty sure the pseudocode is right anyway :slight_smile:

I’ll try to clarify it further by means of an example…

Let’s say that the scenario is that you have a postOp callback where in some
cases you need to pend the request and let it be processed by a worker
thread for some reason and you need to allocate some memory for the
parameters for that worker thread. The worker thread function should look
something like this:

WorkerThreadFunction(params) {
// do actual work
Process(params);

//we’re done, complete the request. Since we’re in a postOp callback and
// in a workerthread we know we must be pended so we need to call:
FltCompletePendedPostOperation();
}

Your postOp would look something like this:

MyPostOpCallback(CallbackData,…)
{
If (some_condition(CallbackData)) {
// you need to process the request later…
Allocate(params);
QueueWork(WorkerThreadFunction , params);
Return FLT_POSTOP_MORE_PROCESSING_REQUIRED;
}

Return FLT_POSTOP_FINISHED_PROCESSING;
}

This is all fine until you decide to filter some operation where the
PostOpCallback can be called at DPC and you can’t allocate memory. So this
is when FltDoCompletionProcessingWhenSafe comes into play. You can create a
new postOp callback that only calls your existing one (again, this is an
example, you can do something else entirely):

DPCSafePostOpCallback(CallbackData,…) {
FLT_POSTOP_CALLBACK_STATUS retStatus = FLT_POSTOP_FINISHED_PROCESSING;

if (!FltDoCompletionProcessingWhenSafe( Data, …, MyPostOpCallback,
retStatus)) {
//FltDoCompletionProcessingWhenSafe can fail in some cases, see
documentation
HandleFailure();
}

Return retStatus;
}

So now what happens is :

  1. If the request is at DPC then FltMgr will post a worker thread to call
    MyPostOpCallback at a safe IRQL and return FLT_MORE_PROCESSING_REQUIRED
    (pending the request). Then, when MyPostOpCallback() returns, FLtMgr will
    look at the status it returned and if the status is
    FLT_POSTOP_FINISHED_PROCESSING then it knows that your minifilter is done
    with the request and FltMgr can call FltCompletePendedPostOperation(). If
    you return FLT_POSTOP_MORE_PROCESSING_REQUIRED then FLtMgr knows that you’re
    still working on that operation (N.B. you might have already finished
    working on it on a different thread) and so FltMgr can’t call
    FltCompletePendedPostOperation() anymore.
  2. If the request is not a DPC then FltMgr will call MyPostOpCallback inline
    and will return whatever MyPostOpCallback() returns.

The documentation line that got you confused is this:
“If the minifilter does return FLT_POSTOP_MORE_PROCESSING_REQUIRED from the
SafePostCallback, the minifilter must call FltCompletePendedPostOperation to
resume completion processing.”

This means that if MyPostOpCallback returns FLT_POSTOP_FINISHED_PROCESSING
then FLtMgr will call FltCompletePendedPostOperation() for you behind the
scenes. However, if MyPostOpCallback returns
FLT_POSTOP_MORE_PROCESSING_REQUIRED (because some_condition() returned TRUE)
then FltMgr is no longer responsible for calling
FltCompletePendedPostOperation() and you must do it yourself.

From an architectural perspective, this makes sense because
MyPostOpCallback() itself doesn’t know whether it’s called inline or from a
pended thread so it wouldn’t know when it needs to call
FltCompletePendedPostOperation() so that’s why FLtMgr calls it behind the
scenes (because FLtMgr knows when it posted to a worker thread).

Bottom line is: if without FltDoCompletionProcessingWhenSafe() in the
picture you never had to call FltCompletePendedPostOperation() then you
still won’t need to.

I hope this makes sense…

Thanks,
Alex.

I got it alex now. Basically it’s a responsibility of a thread who does the work, it may be Filter managers then or may be mine (like the you have illustrated in above example). Thanks for the help.