irp_mj_close vs irp_mj_cleanup

Hi All,

What is the difference between IRP_MJ_CLOSE and IRP_MJ_CLEANUP?

With Regards,
A.Ilamparithi.

A.Ilamparithi wrote:

Hi All,

What is the difference between IRP_MJ_CLOSE and IRP_MJ_CLEANUP?

IRP_MJ_CLEANUP is sent when the last user handle is closed;
when the last reference goes away and the FILE_OBJECT is
about to be torn down, IRP_MJ_CLOSE is sent. You can
receive I/O requests from kernel components between IRP_MJ_CLEANUP
and IRP_MJ_CLOSE.

A good example of this happens when a user mode component:

  • HANDLE hFile = CreateFile()
  • HANDLE hSection = CreateFileMapping();
  • PVOID pvFile = MapViewOfFile();
  • CloseHandle(hSection);
  • CloseHandle(hFile);
  • Manipulate file contents through pvFile.

In this case, you will see IRP_MJ_CLEANUP on the FILE_OBJECT
when the user closes the file handle, but won’t see IRP_MJ_CLOSE
until (at some future time) the user calls UnmapViewOfFile().
You will continue to see paging I/O on the file until the CLOSE.

  • Joseph

The first is kinda a “destructor” for a file object. It is sent just before
the file object is destroyed. The driver can clean up its per-file context in
this. It is the last IRP sent to this file object.

The second is a notification on “all handles closed for a file object”. The
usual operations on CLEANUP is cancelling all pending IO for this file object.

In wast majority of cases, CLOSE is sent just after CLEANUP. But there are
exceptions. For instance, your driver can be opened by another driver using
IoGetDeviceObjectPointer. In this case, you will get CREATE and then CLEANUP
immediately, and after this you will get the flow of IRPs to your file object.
You will get CLOSE much later, when the upper driver will deref the file
object.

Another notable exception is FSDs who support memory-mapped files and use
the Cache Manager. In this case, CLEANUP is sent after the user app closes the
handle, but the file object can be alive and referenced by MM (to support
memory-mapped file) or Cache Manager (which keeps the uninitialized cache map
for some time to optimize the frequent reopens).

In this case, you can have CLOSE hours later then CLEANUP. In between, you
can have paging (and only paging) IO requests to this file object - both reads
and writes.

For an FSD, the CLEANUP handler must at least call CcUninitializeCacheMap.
Otherwise, the reference will persist forever and CLOSE will never follow.

The references in the FSDs are held like:

  • the memory-mapped file VAD holds a reference on “control area”.
  • the cache map holds 1 reference on itself just due to being not
    uninitialized yet. Uninitialize derefs it and puts to some garbage-collection
    list in Cc to be destroyed later.
  • the cache map holds a reference on “control area”.
  • the section object holds a reference on “control area”.
  • the “control area” holds a reference on a file object used to create it
    (in MmCreateSection, which is called by CcInitializeCacheMap or by
    CreateFileMapping from user mode).
  • the file object holds a reference on FCB.

The destruction order is:

  • closing a user handle sends CLEANUP
  • CLEANUP path in the FSD calls CcUninitializeCacheMap, which moves the
    cache map to the list of the Cc’s garbage collector.
  • after all VADs are unmapped, and the uninitialized cache map is
    garbage-collected by Cc - the “control area” is derefed to zero.
  • this cause derefing of the file object used to create the 'control area",
    which usually derefs it to zero and sends CLOSE
  • CLOSE handler derefs the FCB
  • if there are no other file objects referencing this FCB (uncached
    opens) - then the CLOSE handler destroys the FCB.

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