IoCompleteRequest while holding a spinlock?

Hi,

referring to MS KB article Q186775, item 20 - why and how?

I’m done exactly this inside my DPC to complete pending IRPs which I store
in a list (which is protected by a spinlock). It’s not too much of a problem
to rewrite, but I’d still like to know what could possibly happen.

// Johan

Here is an interesting argument between two pundits on this issue.

-Eliyas

Subject: Re: Calling IoCompleteRequest Whilst Holding a Spin Lock
From: xxxxx@cmkrnl.com (Jamie Hanrahan)
Date: 1999/11/04
Newsgroups: comp.os.ms-windows.programmer.nt.kernel-mode
On Thu, 4 Nov 1999 12:46:08 -0500, “Peter Viscarola”

wrote:
>Every once in a while in this group I read a post, often by somebody
whose
>opinion I value, that it is a Very Bad Thing to call
IoCompleteRequest()
>while holding a spin lock.
>
>I do not understand this assertion. Would someone be so kind as to
explain
>it to me?
>
>If it is “legal” to call IoCompleteRequest() at IRQL DISPATCH_LEVEL,
and if
>the Spin Lock in question is a spinlock internal to my driver, I really
can
>think of reason why calling IoCompleteRequest() while holding a spin
lock
>could be implicitly Evil.
>
>Sure, I run the risk of deadlocking IN MY DRIVER if I don’t know what
I’m
>doing. Fine. I accept that.
>
>Please, nobody say “it says not to do this in the DDK.” I really,
honestly,
>don’t give a fat frogs behind what it says in the DDK on this topic.

There are two issues here.

One: You’re holding a spinlock, most commonly your IRP queue spinlock,
and you call IoCR. (Or IoCallDriver, which could result in an immediate
IoCR.) Completion of the IRP could cause an upper layer’s IO completion
routine to send you another IRP, before your call to IoCompleteRequest
returns – i.e., before you can drop the spinlock in that path. Your
dispatch routine for the new IRP has no idea that it’s been called
“within” your call to IoCR, so tries to acquire the same spinlock to
synch
with your IRP state info… deadlock.

This is difficult (no, not impossible) to cover via “I know what I’m
doing”. (One way to cover it, of course, is to build your own
“VMS-style
spinlock” that can tolerate multiple acquisitions by the same CPU. :slight_smile:

Two - and this is much softer - the doc actually goes much farther,
claiming you should never call out of the driver while holding ANY
spinlock.

Now before you say “I don’t want to hear it”, I agree, this is obviously
bogus – the counterexample of KeReleaseSpinLock comes immediately to
mind!

The real issue is that they don’t want us doing things that can take an
unknown, unpredictable, possibly-a-lot-longer-than-expected, etc.,
period
of time, while we hold a spinlock. This seems to me to be a valid
concern, and IoCompleteRequest, cruising as it does through any number
of
drivers’ I/O completion routines, seems to me to be in that category.

Now of course, as you’ve said often, you gotta do what you gotta do.
And
if you DO know what you’re doing, you can break a lot more of the DDK
doc’s rules than this one.

However, as for this one… I’ve always been able to find a cheap way
around any apparent need to hold a spinlock while calling IoCR. So,
since compliance with this rule (however valid the rule might be) isn’t
a
problem for me, I haven’t spent much time thinking about safe ways to
break the rule – or exceptions to the rule, if you prefer that
phraseology.

Like it says in the song… How about you?

— Jamie Hanrahan, Kernel Mode Systems, San Diego CA
Windows NT/2000 driver consulting and training
http://www.kernel-mode.com/

-----Original Message-----
From: xxxxx@esrange.ssc.se [mailto:xxxxx@esrange.ssc.se]
Sent: Monday, November 27, 2000 11:37 PM
To: NT Developers Interest List
Subject: [ntdev] IoCompleteRequest while holding a spinlock?

Hi,

referring to MS KB article Q186775, item 20 - why and how?

I’m done exactly this inside my DPC to complete pending IRPs which I
store
in a list (which is protected by a spinlock). It’s not too much of a
problem
to rewrite, but I’d still like to know what could possibly happen.

// Johan


You are currently subscribed to ntdev as: xxxxx@microsoft.com
To unsubscribe send a blank email to $subst(‘Email.Unsub’)

> referring to MS KB article Q186775, item 20 - why and how?

I’m done exactly this inside my DPC to complete pending IRPs which I store
in a list (which is protected by a spinlock). It’s not too much of a
problem
to rewrite, but I’d still like to know what could possibly happen.

IoCompleteRequest will call some arbitrary completion routines of the upper
drivers. These routines can be complex, and calling complex code while
holding a spinlock is a bad idea.
Also - these routines can decide to submit some IRP (maybe the same IRP) to
your driver’s dispatch entry points. If these entry points also acquire the
same spinlock - you will have a deadlock on the SMP kernel.

Max

Thank you.

Now I understand the issues and I will fix this in my code (even though my
driver is a monolithic driver and should never have any upper drivers). It’s
just a matter of moving the queued IRPs to another local list and releasing
the spinlock before completing them.

// Johan

-----Original Message-----
From: Maxim S. Shatskih [mailto:xxxxx@storagecraft.com]
Sent: den 28 november 2000 18:38
To: NT Developers Interest List
Subject: [ntdev] Re: IoCompleteRequest while holding a spinlock?

> referring to MS KB article Q186775, item 20 - why and how?
>
> I’m done exactly this inside my DPC to complete pending
IRPs which I store
> in a list (which is protected by a spinlock). It’s not too much of a
problem
> to rewrite, but I’d still like to know what could possibly happen.

IoCompleteRequest will call some arbitrary completion
routines of the upper
drivers. These routines can be complex, and calling complex code while
holding a spinlock is a bad idea.
Also - these routines can decide to submit some IRP (maybe
the same IRP) to
your driver’s dispatch entry points. If these entry points
also acquire the
same spinlock - you will have a deadlock on the SMP kernel.

Max


You are currently subscribed to ntdev as: xxxxx@esrange.ssc.se
To unsubscribe send a blank email to $subst(‘Email.Unsub’)