Problem delaying the completion of an Irp

Hi, all

in a Win2k filter driver layered above serenum/serial, I have the
following
need:
when a Create (Open) Irp is received by my filter, I have to let the
lower
drivers handle it and, if the operation is successful, I have to send
some
IOCtls to the serial driver *before* any other operation is performed by
the application that opened the serial port.

I can’t send IOCtls to the serial port I’m filtering before it is open,
so
I’ve tried the following approach:

  • I set a completion routine for the Create IRP
  • in my completion routine, I check if the open was successful and, if
    so,
    i save the Create Irp address, start a worker thread and return
    STATUS_MORE_PROCESSING_REQUIRED (without pending the Irp); according
    to
    the docs, this should stop the completion chain for the Irp until
    someone
    calls again IoCompleteIrp for it
  • in the worker thread, I send the IOCtls needed for my filter
    initializa-
    tion, then I complete the Create Irp previously saved by calling
    IoCompleteIrp

Testing it, I see that after the worker thread completes the Create Irp,
an
access violation occurs with the same Irp used for an IOCtl from the
application that opened the serial Port (the Irp seems corrupt: the
CurrentStackPosition pointer is zero)
I’ve also noticed that when my worker thread completes the Create Irp,
the
values of some fields in it are different from when I return
STATUS_MORE_PROCESSING_REQUIRED to delay the completion. It seems that
the
Irp is modified (reused??) in the interval between the completion
routine
returning STATUS_MORE_PROCESSING_REQUIRED and the worker thread
effectively
completing it, whilst I assumed from the docs that the driver returning
STATUS_MORE_PROCESSING_REQUIRED retained the Irp ownership until the Irp
was effectively completed.

Did I misinterpret the docs, or anyway miss something, or can someone
suggest an alternate approach?

Thank you in advance

Carlo Andreoli

Carlo,

How is it you got from the docs that you could delay the completion of
this IRP without pending the IRP? By queuing a work item and not pending
the IRP you are most certainly causing the system to complete the IRP in a
context other than the IRP’s original context which will most certainly
cause an access violation. For this case you should in your DISPATCH
routine for the Create IRP, call IoMarkIrpPending on the IRP and return
STATUS_PENDING. Then still return STATUS_MORE_PROCESSING_REQUIRED from
the completion routine as you are doing.

Bill M.

Hi, all

in a Win2k filter driver layered above serenum/serial, I have the
following
need:
when a Create (Open) Irp is received by my filter, I have to let the
lower
drivers handle it and, if the operation is successful, I have to send
some
IOCtls to the serial driver *before* any other operation is performed by
the application that opened the serial port.

I can’t send IOCtls to the serial port I’m filtering before it is open,
so
I’ve tried the following approach:

  • I set a completion routine for the Create IRP
  • in my completion routine, I check if the open was successful and, if
    so,
    i save the Create Irp address, start a worker thread and return
    STATUS_MORE_PROCESSING_REQUIRED (without pending the Irp); according
    to
    the docs, this should stop the completion chain for the Irp until
    someone
    calls again IoCompleteIrp for it
  • in the worker thread, I send the IOCtls needed for my filter
    initializa-
    tion, then I complete the Create Irp previously saved by calling
    IoCompleteIrp

Testing it, I see that after the worker thread completes the Create Irp,
an
access violation occurs with the same Irp used for an IOCtl from the
application that opened the serial Port (the Irp seems corrupt: the
CurrentStackPosition pointer is zero)
I’ve also noticed that when my worker thread completes the Create Irp,
the
values of some fields in it are different from when I return
STATUS_MORE_PROCESSING_REQUIRED to delay the completion. It seems that
the
Irp is modified (reused??) in the interval between the completion
routine
returning STATUS_MORE_PROCESSING_REQUIRED and the worker thread
effectively
completing it, whilst I assumed from the docs that the driver returning
STATUS_MORE_PROCESSING_REQUIRED retained the Irp ownership until the Irp
was effectively completed.

Did I misinterpret the docs, or anyway miss something, or can someone
suggest an alternate approach?

Thank you in advance

Carlo Andreoli

Hi, Bill

first of all, thank you for your suggestion. I tried returning
STATUS_PENDING from my Dispatch routine for the Create Irp (also
calling IoMarkIrpPending) and now I’m able to successfully delay
the Create Irp.

How is it you got from the docs that you could delay the completion of
this IRP without pending the IRP? By queuing a work item and
not pending
the IRP you are most certainly causing the system to complete
the IRP in a
context other than the IRP’s original context which will most
certainly
cause an access violation. For this case you should in your DISPATCH
routine for the Create IRP, call IoMarkIrpPending on the IRP
and return
STATUS_PENDING. Then still return
STATUS_MORE_PROCESSING_REQUIRED from
the completion routine as you are doing.

In fact, reading the docs didn’t solve my doubts. In all other Irps
I set a completion routine for, I simply ended the dispatch routine
with a “return IoCallDriver(…)”; this should work since I never had
to further delay the Irp completion in my completion routine.
In the case of the Create Irp, instead, IoCallDriver() surely doesn’t
return STATUS_PENDING, so mending the dispatch routine as you suggested
solved the problem. My doubt is now the following: suppose I can’t tell
until my completion routine is called if the Irp completion has to be
delayed; in this case I have to pend the Irp anyway in the dispatch
routine and return from the completion routine STATUS_SUCCESS (or error)
if the Irp has to be completed immediately and STATUS_MORE_PROCESSING_
REQUIRED if I need to delay the completion?

Thank you again

Carlo Andreoli

Carlo,

If there is even a remote chance you might delay the completion of the
create IRP to some time after your completion routine returns, then you
must, from your dispatch routine, call IoMarkIrpPending and return
STATUS_PENDING explicitly. This is not documented very well, and I
believe I entered a bug on this in the bug bash.

Normally, your completion routine will complete the IRP, so you can just
return what is returned to you from your IoCallDriver call in your
dispatch routine. If the IRP is not completed in the completion routine,
then your return from IoCallDriver may not reflect the true status of the
IRP, in that you may get a STATUS_SUCCESS return from IoCallDriver, even
though your driver has yet to complete the IRP. Then, if you return this
STATUS_SUCCESS from your dispatch routine in this case the driver layered
over yours will not know the IRP is still pending in your driver.

To understand why this is so, I highly recommend you read this:

http://www.osr.com/ntinsider/1997/iocomp/iocomp.htm

Its complicated stuff, you definitely aren’t the first to get your head
wrapped around the axle on this one.

Interestingly enough, everyone working on WDM drivers that are power
policy owners, (that is most device drivers), should have run into this by
now. You cannot properly handle system power IRPs without hitting this
situation. According to the DDK docs system power IRPs should be
completed by power policy owners in the associated device power IRP’s
power completion routine. But alas, most drivers aren’t doing this,
including most DDK samples.

Bill M.

In fact, reading the docs didn’t solve my doubts. In all other Irps
I set a completion routine for, I simply ended the dispatch routine
with a “return IoCallDriver(…)”; this should work since I never had
to further delay the Irp completion in my completion routine.
In the case of the Create Irp, instead, IoCallDriver() surely doesn’t
return STATUS_PENDING, so mending the dispatch routine as you suggested
solved the problem. My doubt is now the following: suppose I can’t tell
until my completion routine is called if the Irp completion has to be
delayed; in this case I have to pend the Irp anyway in the dispatch
routine and return from the completion routine STATUS_SUCCESS (or error)
if the Irp has to be completed immediately and STATUS_MORE_PROCESSING_
REQUIRED if I need to delay the completion?

Thank you again

Carlo Andreoli