InterlockedPushEntrySList() vs ExInterlockedPushEntryList()

My understanding is that InterlockedPushEntrySList() and ExInterlockedPushEntryList() (or ExInterlockedPushEntrySList()) are both available to KM drivers, both atomically insert an item at the top of a singly linked list, but ExInterlockedPushEntryList() uses a spinlock, where InterlockedPushEntrySList() is non-blocking.

Is my understanding correct? If so, is there a reason I would choose a blocking version over a non-blocking one?

The docs.microsoft.com pages for the non-blocking API are aimed at user land usage, which doesn’t fill me with confidence. E.g. The examples are clearly user mode code, and there are no IRQL specs at the bottom of the pages.

They’ve been the same implementation for quite a while (maybe XP?). For example, here how wdm.h defines them for the x64:

#define ExInterlockedPushEntrySList(Head, Entry, Lock) \
    ExpInterlockedPushEntrySList(Head, Entry)
...
#define InterlockedPushEntrySList(Head, Entry) \
    ExpInterlockedPushEntrySList(Head, Entry)

I understand, but what factors would lead to choosing one over the other? My understanding is that the non-blocking interlocked API functions are more efficient than those that use locking, especially the Ex… version here that must transition to DISPATCH_LEVEL IRQL in order to add or remove entries

ExInterlockedPushEntrySList doesn’t use the lock.

#define ExInterlockedPushEntrySList(Head, Entry, Lock) \
    ExpInterlockedPushEntrySList(Head, Entry)

@“Scott_Noone_(OSR)” said:
ExInterlockedPushEntrySList doesn’t use the lock.

Doh! I didn’t even look at what it was defined as. Sigh. Thanks for pointing that out.