Some questions on FltQueueDeferredIoWorkItem

Hi everybody!
I read the documentation of FltQueueDeferredIoWorkItem and saw that it
returns STATUS_FLT_NOT_SAFE_TO_POST_OPERATION if the TopLevelIrp != NULL or
it is a paging i/o operation.

Can somebody explain the reason as to why it is unsafe to post these kind of
operations to a worker thread?

Thanks.

Hi everybody!
I read the documentation of FltQueueDeferredIoWorkItem and saw that it
returns STATUS_FLT_NOT_SAFE_TO_POST_OPERATION if the TopLevelIrp != NULL or
it is a paging i/o operation.

Can somebody explain the reason as to why it is unsafe to post these kind of
operations to a worker thread?

Thanks.

Possible deadlock. I think you want to call the API in pre-write, as
indicated by your previous post, but there is no need for that. You can do any
processing you need in pre-write or post-write directly.

Kernel Developer wrote:

Hi everybody!
I read the documentation of FltQueueDeferredIoWorkItem and saw that it
returns STATUS_FLT_NOT_SAFE_TO_POST_OPERATION if the TopLevelIrp != NULL or it
is a paging i/o operation.

Can somebody explain the reason as to why it is unsafe to post these kind of
operations to a worker thread?


Kind regards, Dejan
http://www.alfasp.com
File system audit, security and encryption kits.

Thanks Dejan!

I cannot do the processing in Pre-Write because i am also getting Paging
Writes, which by nature, have TopLevelIrp != NULL. And issuing any file
system call ( reading in my case ) may lead to a deadlock.
So, that is why i thought of a solution that i am mentioning below. I wanted
to know if it is correct or not, with corresponding reasons.

PreWriteCallback()
{
if( request is paging write or TopLevelIrp != NULL ) {
// Since almost all paging requests have TopLevelIrp != NULL
// allocate and queue a generic work item to read the file.

workitem = FltAllocateGenericWorkItem();
context = event;

FltQueueGenericWorkItem( workitem,…,context);

KeWaitForSingleObject( event );
}
else{
Read the file;
}
return PREOP_SUCCESS_WITH_CALLBACK;
}

WorkerThread( context )
{
Read the file;
event = context;
KeSetEvent( event );
FreeGenericWorkItem;
}

Hi!

If TopLevelIrp field of the current thread is NOT NULL, i.e.,
IoGetTopLevelIrp returns a non-NULL value, the current
thread holds resources above the file system. Thus, you should not try to
perform these operations in another thread (system worker thread in this
case), as you can lead to a deadlock trying to grab the same resources now
from the worker thread.

As far as pending the Paging I/O requests is concerned; it is not possible
because completion of Paging I/O requests require special kernel APC, which
cannot be processed if you post the I/O to worker thread, and hence the
system will deadlock.

Cheers!
Ayush Gupta

> // Since almost all paging requests have TopLevelIrp != NULL

Good comment, BTW.

Almost. Not all.

2 examples:

a) some FSDs - like NT4 FASTFAT - have too short FastIoDispatch, which has no
AcquireForCcFlush. In this case, FsRtlAcquireFileForCcFlush will do the default
processing of just acquiring the FCB locks. In this case, TopLevelIrp is not
set to non-NULL (possibly a bug in FsRtlAcquireFileForCcFlush).

So, locks are acquired, and TopLevelIrp is NULL.

b) XP+ registry uses memory-mapped regions in its implementation. These pages
are also flushed with FCB locks acquired and TopLevelIrp is NULL (maybe the
latter is because the registry flusher is considered to be “top level writer”).

So, sometimes TopLevelIrp is NULL, and the FCB locks are acquired, which
creates the deadlock potential.


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

Thanks Maxim…

This point ( ALMOST all Paging I/O have TopLevelIrp!= NULL ) was stressed by Slava in one of my previous queries, and hence i remembered it.

However, can i do the following to read a file in my pre-write callback. Instead of queuing the actual callback data, i will allocate a generic work item. In the worker thread i will read the file and set an event on which the pre-callback write would be waiting. I am putting down the logic. Please tell me if the logic is correct or not.

PreWriteCallback()
{
if( request is paging write or TopLevelIrp != NULL ) {
// Since almost all paging requests have TopLevelIrp != NULL
// allocate and queue a generic work item to read the file.

workitem = FltAllocateGenericWorkItem();
context = event;

FltQueueGenericWorkItem( workitem,…,context);

KeWaitForSingleObject( event );
}
else{
Read the file;
}
return PREOP_SUCCESS_WITH_CALLBACK;
}

WorkerThread( context )
{
Read the file;
event = context;
KeSetEvent( event );
FreeGenericWorkItem;
}

I don’t think reading from the system thread during paging I/O is safe (or
even safer) than reading from the paging thread.
For a special purpose encryption filter (which is not used in heavy duty
environments, but the env. is high security), we read the file during paging
write (legacy filter - we used the original IRP, changed buffer and MajorFunc,
then sent the read - reverted to write and sent it down). That worked, never had
any deadlocks even under stress test.

Regards, Dejan.

Kernel Developer wrote:

Thanks Dejan!

I cannot do the processing in Pre-Write because i am also getting Paging
Writes, which by nature, have TopLevelIrp != NULL. And issuing any file
system call ( reading in my case ) may lead to a deadlock.
So, that is why i thought of a solution that i am mentioning below. I wanted
to know if it is correct or not, with corresponding reasons.

PreWriteCallback()
{
if( request is paging write or TopLevelIrp != NULL ) {
// Since almost all paging requests have TopLevelIrp != NULL
// allocate and queue a generic work item to read the file.

workitem = FltAllocateGenericWorkItem();
context = event;

FltQueueGenericWorkItem( workitem,…,context);

KeWaitForSingleObject( event );
}
else{
Read the file;
}
return PREOP_SUCCESS_WITH_CALLBACK;
}

WorkerThread( context )
{
Read the file;
event = context;
KeSetEvent( event );
FreeGenericWorkItem;
}


NTFSD is sponsored by OSR

For our schedule debugging and file system seminars
(including our new fs mini-filter seminar) visit:
http://www.osr.com/seminars

You are currently subscribed to ntfsd as: xxxxx@alfasp.com
To unsubscribe send a blank email to xxxxx@lists.osr.com


Kind regards, Dejan
http://www.alfasp.com
File system audit, security and encryption kits.

Thanks Dejan!

I already knew this solution for the legacy filters. In a previous discussion, Maxim had proposed this solution. But since i am developing a minifilter rather than a full blown legacy filter, i cant do this stuff. Right?
Is there any other solution?

FltReadFile can do the same thing… set Instance to your Instance, you would get the same effect as the legacy solution.

xxxxx@yahoo.co.in wrote:

Thanks Dejan!

I already knew this solution for the legacy filters. In a previous discussion, Maxim had proposed this solution. But since i am developing a minifilter rather than a full blown legacy filter, i cant do this stuff. Right?
Is there any other solution?


Kind regards, Dejan
http://www.alfasp.com
File system audit, security and encryption kits.

Thanks Dejan!
But what about the problems of TopLevelIrp != NULL and thus a possibility of a deadlock on issuing FltReadFile in a precallback routine for a paging write?

It’s possible, but we never stumbled across a deadlock for this scenario.
Checking FastFat code, I ran a few scenarios (cached/non-cached opens simultaneously, read/write requests, CcFlush calls as a result, I don’t see where the
call would cause a deadlock).
It’s possible I missed/miscalculated a scenario, but this code has ran on a lot of systems for 2 years now. (granted again, none of those were heavy-duty
systems, the number of read/write requests processed this way was tiny).

xxxxx@yahoo.co.in wrote:

Thanks Dejan!
But what about the problems of TopLevelIrp != NULL and thus a possibility of a deadlock on issuing FltReadFile in a precallback routine for a paging write?


Kind regards, Dejan
http://www.alfasp.com
File system audit, security and encryption kits.

One more thing Dejan…
How did you manage to use FltReadFile in pre-callback for Paging Write?
It comes at IRQL = APC_LEVEL… And FltReadFile can be called only at PASSIVE_LEVEL.

I called IoCallDriver in a legacy filter :wink: There were a few WRITE calls at
APC_LEVEL, but that was a really small number, TLI != NULL as well, still no locks.
I didn’t try calling FltReadFile at APC_LEVEL, and if FltMgr denies such calls, I
can’t help you :frowning:

xxxxx@yahoo.co.in wrote:

One more thing Dejan…
How did you manage to use FltReadFile in pre-callback for Paging Write?
It comes at IRQL = APC_LEVEL… And FltReadFile can be called only at PASSIVE_LEVEL.


Kind regards, Dejan
http://www.alfasp.com
File system audit, security and encryption kits.