On 11/05/2016 02:20 PM, Anatoly Mikhailov wrote:
Yes. That means CcPurgeCacheSection can’t fail. It invalidates pages but
not allocates them. Cleanup function must not fail by its nature.
This is not correct.
Obviously in an ideal world cleanup functions couldn’t fail, but every
system is full of less than ideal situations, and in NT, this is one.
The original design of NT allowed virtual address mappings to reference
physical pages, with no reverse mapping allowing a physical page to
identify its virtual address users. The issue this created for purge is
that if a physical page is actively mapped, the physical page couldn’t
be discarded and repurposed because it’s in use, and there was no
mechanism to identify and clean up the user, so purge just fails.
In Win7 a new reverse mapping mechanism was added which allowed a given
section to identify user mappings and allow virtual address references
to be removed. This is not used via the regular CcPurgeCacheSection but
is available through CcCoherencyFlushAndPurgeCache (which CcSetFileSizes
doesn’t use.)
However, note that there’s still a synchronization problem: a page can
become in use by a user process touching a virtual address range,
something the file system can’t synchronize against. It can even map a
new virtual address range, which the file system receives no
notification for. So even with CcCoherencyFlushAndPurgeCache, there’s a
chance that a page reference can be trimmed and immediately recreated,
causing purge to fail; and it’s why CcCoherencyFlushAndPurgeCache is
almost always called in a loop. The loop is there to catch soft faults
resolving the virtual address to page mapping immediately. The FS also
needs synchronization around purge to prevent hard faults from bringing
data back from disk immediately.
The original way NT was intending to make truncation work is via
MmCanFileBeTruncated. Noting that the file system receives no
notification for virtual address mappings, this API required the FS to
hold synchronization against section creation and return FALSE if an
existing user had a section handle. If that section handle exists, that
caller is free to map a virtual address range and cause purge to fail.
This is of course extremely problematic since it means truncation can
fail semi-randomly, or even, quite deterministically.
Because CcSetFileSizes is part of the original NT, which theorized that
purges couldn’t fail in this path due to MmCanFileBeTruncated,
CcSetFileSizes made no attempt to communicate purge failure. It won’t
raise, but it can just leave pages around unexpectedly.
So in Vista, CcSetFileSizesEx made its debut, which added a return value
indicating purge failure. Unfortunately this didn’t really improve
anything, because purge isn’t atomic - the memory manager will start
throwing away dirty data, then get to a page that’s mapped somewhere,
and fail - but the FS can’t abort the operation because data is already
gone. So this API turned out to not be that useful, and doesn’t appear
to be documented anywhere either.
Now all of that is on the record, going back to the original question…
The best I could really do was to ensure that CcSetFileSizes is called
early when extending so that on allocation failure the extend operation
could be failed. However, when truncating it’s important to perform the
truncation and commit it first because once purging starts it would be
invalid to roll back a truncation, so the same API gets called very
differently for extend vs. truncate. NTFS is still relying on
MmCanFileBeTruncated to reduce the risk of spurious purge failure.
Note the issue with purge failure on truncate isn’t immediate - it might
leave data beyond file size which can’t be read directly and can’t be
written to disk since there’s no allocation, but the real problem is
what happens when the file is extended again and those stale pages
become reachable. I can’t help but think the “best” way to handle this
today is do the purge as part of the extension (and check for failure)
to clean up after a previous truncate, and throw MmCanFileBeTruncated to
the curb.
Good luck,
–
http://www.malsmith.net