keentercriticalregion and exacquirefastmutex

Would someone be kind enough to explain the WHY of sample code that does the following:

KeEnterCriticalRegion()
ExAcquireFastMutex()

deep thoughts here

ExReleaseFastMutex()
KeLeaveCriticalRegion();

I’ve got a snippet of the OSR article pasted in below and I just don’t understand why one of these guys (Mutex or Critical Region) is not sufficient? I’d sure like to understand why both are necessary. Maybe you could throw in an example of what could happen if one or the other is not there.

TIA

Larry.

from the article:
Asynchronous Procedure Calls in Windows NT
Content provided by OSR Open Systems Resources, Inc.

… To alleviate this situation, a typical Windows NT file system disables kernel mode APC delivery by calling KeEnterCriticalRegion(…) (albeit under the name FsRtlEnterFileSystem(…), which in current versions of Windows NT is defined to be KeEnterCriticalRegion(…).) This disables the delivery of kernel mode APCs, although it allows the I/O Manager’s special kernel mode APCs to be delivered. These APCs are safe because they do not re-enter the file system and hence do not introduce any risk of deadlock.

Another way that APC delivery can be disabled is to raise the IRQL of the system to APC_LEVEL. This disables the delivery of all APCs, of any type. For example, the Windows NT Memory Manager issues I/O operations at APC_LEVEL under certain circumstances. This ensures that any APCs, especially I/O completion APCs, are not delivered while it is starting a new paging I/O operation.

Some of the synchronization primitives in Windows NT raise the IRQL of the running system to APC_LEVEL in order to ensure that code cannot re-enter for the currently running thread. The notable case here is the fast mutex operations. ExAcquireFastMutex(…) raises the IRQL of the system to APC_LEVEL, lowering it when the driver calls ExReleaseFastMutex(…). Thus, while a fast mutex is held, all APCs for this thread are not delivered.

I think KeEnterCriticalRegion() is simply unnecessary here. However, if you replace
ExAcquireFastMutex() with ExAcquireFastMutexUnsafe(), KeWaitxxx() or ExAcquireResourcexxx() at IRQL ==PASSIVE_LEVEL, the whole thing starts making a perfect sense (at least to me)…

The rationale behind using KeEnterCriticalRegion() is avoiding deadlocks. Consider the situation when a thread that has acquired a resource for shared access in the routine A receives a kernel-mode APC, and APC routine reenters FSD and calls FSD’s routine B that tries to acquire the same resource exclusively. The resource cannot be acquired exclusively until the thread releases it, the thread cannot release it until APC routine (and, hence, routine B) returns control, and routine B cannot return control until it acquires the resource exclusively. Impasse.

However, if the thread has made KeEnterCriticalRegion() call before acquiring a resource for shared access, the problem is gone - once KeEnterCriticalRegion() disables delivery of “regular” kernel-mode APCs to the target thread, the above scenario just cannot occur( special kernel-mode APCs are still delivered to the thread, but they don’t re-enter the file system, so that we don’t have to be bothered about them in this context).

This is how it works at PASSIVE_LEVEL. If current IRQL is APC_LEVEL, neither “regular” nor special kernel-mode APCs are delivered to the target thread until it lowers IRQL, so that the above scenario is not going to occur anyway. As a very first step, ExAcquireFastMutex() raises IRQL to APC_LEVEL,
and IRQL is not going down to PASSIVE_LEVEL at least until fast mutex gets released. Therefore, calling KeEnterCriticalRegion() before calling ExAcquireFastMutex() is not really needed…

However, if we acquired fast mutex with ExAcquireFastMutexUnsafe(), which does not change IRQL, at IRQL ==PASSIVE_LEVEL ( certainly, we are not supposed to do it, but just in case if we do), or relied upon events/ resources, calling KeEnterCriticalRegion() before acquiring a resource would be quite wise thing to do…

Anton Bassov

Anton,
Thanks for the response. Certainly what you said coincides with the doc’s and my understanding as well. But, when you see the construct as I described it in osr and microsoft samples it certainly gives pause.

Larry

Larry,

I think OSR guys just made a typing mistake, i.e. said ExAcquireFastMutex() at the time when they meant ExAcquireResourcexxx() - it can happen to everyone from time to time…

Anton Bassov