IoReleaseRemoveLockAndWait() does not wait

Hello,

I try to cancel an asynchronous IRP. Before freeing the IRP after canceling it I
wait for an IoCount reaching zero. I am realizing this mechanism by using the
IoAcquireRemoveLock and Release functions.
My IRP-Cancelation looks like this:

CancelIrp()
{
IoAcquireRemoveLock(&lock, NULL);
IoCancelIrp(irp);
IoReleaseRemoveLockAndWait(&lock);

IoFreeIrp(irp);
}

The IRP Completion does this:

OnCompletion()
{

IoReleaseRemoveLock(&lock);
return STATUS_MORE_PROCESSING_REQUIRED;
}

I would consider that the following sequence would take place if the last
outstanding IRP gets canceled:

  1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
  2. IoCancelIrp() succeeds
  3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x1
  4. IoReleaseRemoveLockAndWait() waits for IoCount becomming 0x0 …
  5. OnCompletion()
  6. IoReleaseRemoveLock() –> IoCount 0x1 -> 0x0
  7. IoReleaseRemoveLockAndWait() returns from waiting and Lock gets removed.
  8. Irp can be securely be removed.

But instead of this the following happens:

  1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
  2. IoCancelIrp() succeeds
  3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x0 !!!
  4. IoReleaseRemoveLockAndWait() returns and disables the lock.

Now I get a BugCheck for one of the following
* The lower IoCompletion bugchecks because the irp was freed to early.
* OnCompletion() bugchecks with a BAD_POOL_CALLER because the lock was removed

I hope anyone has a solution for this. I consider that I am using the
IoAcquire/ReleaseRemoveLock functions wrong.

Greetings
Emanuel Eick

Short answer: yes, you are not using the relock correctly. Also, you should always use uniquely paired tag values for acquire/release so that you can catch errors like this under verifier in Vista and later or on a chk build for a prev release

Long answer: remlocks are a little quirky in how they are initialized and destroyed. First, you can only init them at passive which does not allow them to be a generally purpose ref counting with a synchronous deletion object. Second, once a remlock has been ReleleaseAndWait’ed, the only way to reinit is to call InitializeRemLock which has the IRQL restriction. Third and most important to your question, the contract for ReleaseAndWait is that the caller must acquire the remlock before this call is made. This allows for the pnp dispatch routine to always have an acquire rem lock at the top of the function, regardless of pnp minor code.

ReleaseAndWait will release the previous acquire, remove the initial bias on the io count (e.g. subtract one) and if the result is !=0, wait. This is why you see the count go down by 2. To fix your code you should have a remlock acquire before sending the irp and then releasing the remlock in the completion routine. This way the completion routine’s release is paired with the acquire before send, not the acquire for releaseandwait

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@siemens.com
Sent: Friday, May 16, 2008 7:18 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] IoReleaseRemoveLockAndWait() does not wait

Hello,

I try to cancel an asynchronous IRP. Before freeing the IRP after canceling it I
wait for an IoCount reaching zero. I am realizing this mechanism by using the
IoAcquireRemoveLock and Release functions.
My IRP-Cancelation looks like this:

CancelIrp()
{
IoAcquireRemoveLock(&lock, NULL);
IoCancelIrp(irp);
IoReleaseRemoveLockAndWait(&lock);

IoFreeIrp(irp);
}

The IRP Completion does this:

OnCompletion()
{

IoReleaseRemoveLock(&lock);
return STATUS_MORE_PROCESSING_REQUIRED;
}

I would consider that the following sequence would take place if the last
outstanding IRP gets canceled:

  1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
  2. IoCancelIrp() succeeds
  3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x1
  4. IoReleaseRemoveLockAndWait() waits for IoCount becomming 0x0 …
  5. OnCompletion()
  6. IoReleaseRemoveLock() –> IoCount 0x1 -> 0x0
  7. IoReleaseRemoveLockAndWait() returns from waiting and Lock gets removed.
  8. Irp can be securely be removed.

But instead of this the following happens:

  1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
  2. IoCancelIrp() succeeds
  3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x0 !!!
  4. IoReleaseRemoveLockAndWait() returns and disables the lock.

Now I get a BugCheck for one of the following
* The lower IoCompletion bugchecks because the irp was freed to early.
* OnCompletion() bugchecks with a BAD_POOL_CALLER because the lock was removed

I hope anyone has a solution for this. I consider that I am using the
IoAcquire/ReleaseRemoveLock functions wrong.

Greetings
Emanuel Eick


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

I cannot understand how cancellation is related to remove locks.

IoCancelIrp is only a hint for the lower driver, which the lower driver can
ignore.


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

wrote in message news:xxxxx@ntdev…
> Hello,
>
> I try to cancel an asynchronous IRP. Before freeing the IRP after canceling
it I
> wait for an IoCount reaching zero. I am realizing this mechanism by using the
> IoAcquireRemoveLock and Release functions.
> My IRP-Cancelation looks like this:
>
> CancelIrp()
> {
> IoAcquireRemoveLock(&lock, NULL);
> IoCancelIrp(irp);
> IoReleaseRemoveLockAndWait(&lock);
> …
> IoFreeIrp(irp);
> }
>
> The IRP Completion does this:
>
> OnCompletion()
> {
> …
> IoReleaseRemoveLock(&lock);
> return STATUS_MORE_PROCESSING_REQUIRED;
> }
>
> I would consider that the following sequence would take place if the last
> outstanding IRP gets canceled:
> 1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
> 2. IoCancelIrp() succeeds
> 3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x1
> 4. IoReleaseRemoveLockAndWait() waits for IoCount becomming 0x0 …
> 5. OnCompletion()
> 6. IoReleaseRemoveLock() –> IoCount 0x1 -> 0x0
> 7. IoReleaseRemoveLockAndWait() returns from waiting and Lock gets
removed.
> 8. Irp can be securely be removed.
>
> But instead of this the following happens:
> 1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
> 2. IoCancelIrp() succeeds
> 3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x0 !!!
> 4. IoReleaseRemoveLockAndWait() returns and disables the lock.
>
> Now I get a BugCheck for one of the following
> * The lower IoCompletion bugchecks because the irp was freed to early.
> * OnCompletion() bugchecks with a BAD_POOL_CALLER because the lock was
removed
>
> I hope anyone has a solution for this. I consider that I am using the
> IoAcquire/ReleaseRemoveLock functions wrong.
>
> Greetings
> Emanuel Eick
>
>

>which has the IRQL restriction. Third and most important to your question,
the

contract for ReleaseAndWait is that the caller must acquire the remlock before
this
call is made. This allows for the pnp dispatch routine to always have an
acquire
rem lock at the top of the function, regardless of pnp minor code.

Why remlock acquisition is needed in PnP dispatch? Aren’t different PnP minor
codes sychronized by the kernel itself?


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

Only state changing pnp irps are serialized by the kernel (against each other and the entire stack’s state). All other pnp irps (like query interface, query device caps, etc) are NOT synchronized by the kernel. An easy way to tell the difference is in the docs…if it says a driver can send the request, it is not synchronized against any kind of state; if it says drivers cannot set the request it is synchronized.

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of Maxim S. Shatskih
Sent: Saturday, May 17, 2008 3:31 AM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] IoReleaseRemoveLockAndWait() does not wait

which has the IRQL restriction. Third and most important to your question,
the
contract for ReleaseAndWait is that the caller must acquire the remlock before
this
call is made. This allows for the pnp dispatch routine to always have an
acquire
rem lock at the top of the function, regardless of pnp minor code.

Why remlock acquisition is needed in PnP dispatch? Aren’t different PnP minor
codes sychronized by the kernel itself?


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

In addition, this piece of code seems rather suspicious:

CancelIrp()
{
IoAcquireRemoveLock(&lock, NULL);
IoCancelIrp(irp);
IoReleaseRemoveLockAndWait(&lock);

IoFreeIrp(irp);
}

There is no need to acquire remlock before IoCancelIrp() call and
release after. Instead, remlock should be acquired before sending
original IRP and the result of acquire has to be tested and IRP must not
be sent if it fails. That’s the whole point of remlocks.

Next, unconditional IoFreeIrp() is just wrong in this case.

Best regards,

Michal Vodicka
UPEK, Inc.
[xxxxx@upek.com, http://www.upek.com]

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Doron Holan
Sent: Friday, May 16, 2008 6:58 PM
To: Windows System Software Devs Interest List
Subject: RE: [ntdev] IoReleaseRemoveLockAndWait() does not wait

Short answer: yes, you are not using the relock correctly.
Also, you should always use uniquely paired tag values for
acquire/release so that you can catch errors like this under
verifier in Vista and later or on a chk build for a prev release

Long answer: remlocks are a little quirky in how they are
initialized and destroyed. First, you can only init them at
passive which does not allow them to be a generally purpose
ref counting with a synchronous deletion object. Second,
once a remlock has been ReleleaseAndWait’ed, the only way to
reinit is to call InitializeRemLock which has the IRQL
restriction. Third and most important to your question, the
contract for ReleaseAndWait is that the caller must acquire
the remlock before this call is made. This allows for the
pnp dispatch routine to always have an acquire rem lock at
the top of the function, regardless of pnp minor code.

ReleaseAndWait will release the previous acquire, remove the
initial bias on the io count (e.g. subtract one) and if the
result is !=0, wait. This is why you see the count go down
by 2. To fix your code you should have a remlock acquire
before sending the irp and then releasing the remlock in the
completion routine. This way the completion routine’s
release is paired with the acquire before send, not the
acquire for releaseandwait

d

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@siemens.com
Sent: Friday, May 16, 2008 7:18 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] IoReleaseRemoveLockAndWait() does not wait

Hello,

I try to cancel an asynchronous IRP. Before freeing the IRP
after canceling it I
wait for an IoCount reaching zero. I am realizing this
mechanism by using the
IoAcquireRemoveLock and Release functions.
My IRP-Cancelation looks like this:

CancelIrp()
{
IoAcquireRemoveLock(&lock, NULL);
IoCancelIrp(irp);
IoReleaseRemoveLockAndWait(&lock);

IoFreeIrp(irp);
}

The IRP Completion does this:

OnCompletion()
{

IoReleaseRemoveLock(&lock);
return STATUS_MORE_PROCESSING_REQUIRED;
}

I would consider that the following sequence would take place
if the last
outstanding IRP gets canceled:

  1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
  2. IoCancelIrp() succeeds
  3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x1
  4. IoReleaseRemoveLockAndWait() waits for IoCount
    becomming 0x0 …
  5. OnCompletion()
  6. IoReleaseRemoveLock() –> IoCount 0x1 -> 0x0
  7. IoReleaseRemoveLockAndWait() returns from waiting and
    Lock gets removed.
  8. Irp can be securely be removed.

But instead of this the following happens:

  1. IoAcquireRemoveLock() –> lock.Common.IoCount 0x1 -> 0x2
  2. IoCancelIrp() succeeds
  3. IoReleaseRemoveLockAndWait() –> IoCount 0x2 -> 0x0
    !!!
  4. IoReleaseRemoveLockAndWait() returns and disables the lock.

Now I get a BugCheck for one of the following
* The lower IoCompletion bugchecks because the irp was
freed to early.
* OnCompletion() bugchecks with a BAD_POOL_CALLER because
the lock was removed

I hope anyone has a solution for this. I consider that I am using the
IoAcquire/ReleaseRemoveLock functions wrong.

Greetings
Emanuel Eick


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online
at http://www.osronline.com/page.cfm?name=ListServer


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online
at http://www.osronline.com/page.cfm?name=ListServer