>…but the documentation for KeReleaseSpinLock says I must supply the IRQL
returned from the associated call to AcquireSpinLock;
KeAcquireSpinLock() is,basically, just a wrapper for the following sequence:
old_irql=KeRaiseIrql(DISPATCH_LEVEL);
KeAcquireSpinLocAt DpcLevel();
* p_old_irql=old_irql;
Therefore,KeReleaseSpinLock() does the following:
KeReleaseSpinLockFromDpcLevel();
KeLowerIqrl( old_irql);
I hope the above lines are sufficient for realising that, although you can release spinlocks in any order that you wish, irql parameter should be provided in the reverse order of lock acquisition, unless you are absolutely sure the first lock always gets acquired at elevated IRQL (for example, you acquire all your locks in context of DPC routine).
In your particular case you should do the following ( I assume you are not doing it in context of DPC routine):
KeAcquireSpinLock(& lock1, &old_irql1);
KeAcquireSpinLock(& lock2, &old_irql2);
…
KeReleaseSpinLock(lock1, old_irql2);
KeReleaseSpinLock(lock2, old_irql1);
Alternatively, you can simply call KeRaiseIrql(DISPATCH_LEVEL) before you proceed to spinlock acquisition and KeLowerIrql()after both locks have been released. In such case you can simply ignore old_irql parameter, because it becomes meaningless in this scenario (or, even better, use XXXXDpcLevel() routines). I think this approach is better simply because it saves you extra confusion.
What you should NEVER do is the following( again, unless you are 100% sure you always do it at elevated IRQL)
KeAcquireSpinLock(& lock1, &old_irql1);
KeAcquireSpinLock(& lock2, &old_irql2);
…
KeReleaseSpinLock(lock1, old_irql1;
KeReleaseSpinLock(lock2, old_irql2);
If you do it this way and your original IRQL is not elevated you will end up with a spinlock held at IRQL<dispatch_level which is a fatal error because it may result in deadlock>
Anton Bassov</dispatch_level>