Yoyoing an Irp

I am tinkering with a volume filter. Here is what I am doing:

On IRP_MJ_WRITE

  1. allocate new mdl to match the write length parameter.
  2. Save current irp mdl
  3. Replace mdl in irp with new mdl
  4. IoCopyCurrentIrpStackLocationToNext()
  5. IoGetNextIrpStackLocation()
  6. Change irp next sl from a write to a read.
  7. IoSetCompletionRotuine(Irp, read_complete, saved_mdl, TRUE, TRUE, TRUE);
  8. return IoCallDriver()

On IRP_MJ_READ completion

  1. Do something interesting with the data read and then delete the new mdl.
  2. Change the mdl in the irp to the one passed in the context.
  3. IoSkipCurrentIrpStackLocation()
  4. return IoCallDriver()

The code seems to be working OK. Is this safe?

Oops, on the read completion, I do this:

IoCallDriver();
return STATUS_MORE_PROCESSING_REQUIRED

On Sat, Jun 24, 2017 at 12:31 PM wrote:

> I am tinkering with a volume filter. Here is what I am doing:
>
> On IRP_MJ_WRITE
> 1) allocate new mdl to match the write length parameter.
> 2) Save current irp mdl
> 3) Replace mdl in irp with new mdl
> 4) IoCopyCurrentIrpStackLocationToNext()
> 5) IoGetNextIrpStackLocation()
> 6) Change irp next sl from a write to a read.
> 7) IoSetCompletionRotuine(Irp, read_complete, saved_mdl, TRUE, TRUE, TRUE);
> 8) return IoCallDriver()
>
> On IRP_MJ_READ completion
> 1) Do something interesting with the data read and then delete the new mdl.
> 2) Change the mdl in the irp to the one passed in the context.
> 3) IoSkipCurrentIrpStackLocation()
> 4) return IoCallDriver()
>
> The code seems to be working OK. Is this safe?
>
>
> —
> NTDEV is sponsored by OSR
>
> Visit the list online at: <
> http://www.osronline.com/showlists.cfm?list=ntdev&gt;
>
> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
> software drivers!
> Details at http:
>
> To unsubscribe, visit the List Server section of OSR Online at <
> http://www.osronline.com/page.cfm?name=ListServer&gt;
></http:>

And I do NOT mark the irp pending on read complete.

On Sat, Jun 24, 2017 at 12:43 PM Jamey Kirby wrote:

> Oops, on the read completion, I do this:
>
> IoCallDriver();
> return STATUS_MORE_PROCESSING_REQUIRED
>
>
> On Sat, Jun 24, 2017 at 12:31 PM wrote:
>
>> I am tinkering with a volume filter. Here is what I am doing:
>>
>> On IRP_MJ_WRITE
>> 1) allocate new mdl to match the write length parameter.
>> 2) Save current irp mdl
>> 3) Replace mdl in irp with new mdl
>> 4) IoCopyCurrentIrpStackLocationToNext()
>> 5) IoGetNextIrpStackLocation()
>> 6) Change irp next sl from a write to a read.
>> 7) IoSetCompletionRotuine(Irp, read_complete, saved_mdl, TRUE, TRUE,
>> TRUE);
>> 8) return IoCallDriver()
>>
>> On IRP_MJ_READ completion
>> 1) Do something interesting with the data read and then delete the new
>> mdl.
>> 2) Change the mdl in the irp to the one passed in the context.
>> 3) IoSkipCurrentIrpStackLocation()
>> 4) return IoCallDriver()
>>
>> The code seems to be working OK. Is this safe?
>>
>>
>> —
>> NTDEV is sponsored by OSR
>>
>> Visit the list online at: <
>> http://www.osronline.com/showlists.cfm?list=ntdev&gt;
>>
>> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
>> software drivers!
>> Details at http:
>>
>> To unsubscribe, visit the List Server section of OSR Online at <
>> http://www.osronline.com/page.cfm?name=ListServer&gt;
>>
></http:>

Here is a snippet of my code:

NTSTATUS WriteComplete(In PDEVICE_OBJECT DeviceObject, In PIRP Irp,
In PVOID Context) {
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
PDEVICE_EXTENSION device_extension = DeviceObject->DeviceExtension;
PMDL r_mdl = (PMDL)Context;
PUCHAR r_ptr = MmGetSystemAddressForMdlSafe(r_mdl, NormalPagePriority);

if (NT_SUCCESS(Irp->IoStatus.Status) && Irp->IoStatus.Information != 0) {
//
// We’ve preread the data before the write occured.
// We’ve allowed the write to occur.
// Now we are in the final write completion handler
// with the buffer that was read.
//
}

// We do not need our intermediate read buffer anymore.
IoFreeMdl(r_mdl);
ExFreePool(r_ptr);
IoReleaseRemoveLock(&device_extension->remove_lock, NULL);
// Now it is OK to allow the IRP to do normal completion.
return STATUS_CONTINUE_COMPLETION;
UNREFERENCED_PARAMETER(Context);
}

NTSTATUS PreReadComplete(In PDEVICE_OBJECT DeviceObject, In PIRP Irp,
In PVOID Context) {
PMDL r_mdl = Irp->MdlAddress;
PMDL w_mdl = (PMDL)Context;
if (NT_SUCCESS(Irp->IoStatus.Status) && Irp->IoStatus.Information != 0) {
//
// We could do something interesting with the data read here,
// release the memory, and then proceed. However, in this example,
// and as an exercise, I’m passing the preread data all the way
// to the write completion handler.
//
}
// Set IRPs MDL back to the original write MDL.
Irp->MdlAddress = w_mdl;
PDEVICE_EXTENSION device_extension = DeviceObject->DeviceExtension;
// Pass the request along.
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, WriteComplete, r_mdl, TRUE, TRUE, TRUE);
// We must NOT mark the IRP pending here.
IoCallDriver(device_extension->target_device, Irp);
// Halt processing, and take control of the IRP again in the
// second completion handler.
return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS Write(In PDEVICE_OBJECT DeviceObject, In PIRP Irp) {
PDEVICE_EXTENSION device_extension = DeviceObject->DeviceExtension;
NTSTATUS status = IoAcquireRemoveLock(&device_extension->remove_lock, NULL);
if (!NT_SUCCESS(status)) {
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
PIO_STACK_LOCATION c_stack = IoGetCurrentIrpStackLocation(Irp);
ULONG length = c_stack->Parameters.Write.Length;
if (length != 0) {
IoCopyCurrentIrpStackLocationToNext(Irp);
// Our filter device set DO_DIRECT_IO, so we can assume we will
// only receive MDLs in IRPs. This is just for tinkering anyway.

// Save the original MDL that contaisn the data being written.
PMDL w_mdl = Irp->MdlAddress;
// Allocate buffer for intermediate read request.
PVOID ptr = ExAllocatePool(NonPagedPool,
length < PAGE_SIZE ? PAGE_SIZE : length);
// Create an MDL for the intermediate read request.
PMDL r_mdl = IoAllocateMdl(ptr, length, FALSE, FALSE, NULL);
MmBuildMdlForNonPagedPool(r_mdl);
Irp->MdlAddress = r_mdl;
PIO_STACK_LOCATION n_stack = IoGetNextIrpStackLocation(Irp);
// Setup next stack location for read.
n_stack->MajorFunction = IRP_MJ_READ;
n_stack->Parameters.Read.ByteOffset = c_stack->Parameters.Write.ByteOffset;
n_stack->Parameters.Read.Key = c_stack->Parameters.Write.Key;
n_stack->Parameters.Read.Length = c_stack->Parameters.Write.Length;
IoSetCompletionRoutine(Irp, PreReadComplete, w_mdl, TRUE, TRUE, TRUE);
// Mark IRP pending BEFORE passing the request along.
IoMarkIrpPending(Irp);
IoCallDriver(device_extension->target_device, Irp);
// Because we have an intermediate completion handler that
// returns STATUS_MORE_PROCESSING_REQUIRED, we MUST return
// STATUS_PENDING.
return STATUS_PENDING;
}
else {
// Zero byte write; nothing to do.
IoReleaseRemoveLock(&device_extension->remove_lock, NULL);
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(device_extension->target_device, Irp);
}
}

On Sat, Jun 24, 2017 at 12:44 PM Jamey Kirby wrote:

> And I do NOT mark the irp pending on read complete.
>
> On Sat, Jun 24, 2017 at 12:43 PM Jamey Kirby
> wrote:
>
>> Oops, on the read completion, I do this:
>>
>> IoCallDriver();
>> return STATUS_MORE_PROCESSING_REQUIRED
>>
>>
>> On Sat, Jun 24, 2017 at 12:31 PM wrote:
>>
>>> I am tinkering with a volume filter. Here is what I am doing:
>>>
>>> On IRP_MJ_WRITE
>>> 1) allocate new mdl to match the write length parameter.
>>> 2) Save current irp mdl
>>> 3) Replace mdl in irp with new mdl
>>> 4) IoCopyCurrentIrpStackLocationToNext()
>>> 5) IoGetNextIrpStackLocation()
>>> 6) Change irp next sl from a write to a read.
>>> 7) IoSetCompletionRotuine(Irp, read_complete, saved_mdl, TRUE, TRUE,
>>> TRUE);
>>> 8) return IoCallDriver()
>>>
>>> On IRP_MJ_READ completion
>>> 1) Do something interesting with the data read and then delete the new
>>> mdl.
>>> 2) Change the mdl in the irp to the one passed in the context.
>>> 3) IoSkipCurrentIrpStackLocation()
>>> 4) return IoCallDriver()
>>>
>>> The code seems to be working OK. Is this safe?
>>>
>>>
>>> —
>>> NTDEV is sponsored by OSR
>>>
>>> Visit the list online at: <
>>> http://www.osronline.com/showlists.cfm?list=ntdev&gt;
>>>
>>> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
>>> software drivers!
>>> Details at http:
>>>
>>> To unsubscribe, visit the List Server section of OSR Online at <
>>> http://www.osronline.com/page.cfm?name=ListServer&gt;
>>>
>></http:>

If an Irp is being completed at DISPATCH_LEVEL the underlying filters and the volume manager driver will have a hard time to process this reused IRP. The only strategy for them is to mark IRP pending, post it to a worker thread and return immediately. I am not sure they are designed to process write requests with scheduler being disabled.

I guess I don’t understand the question.

Is WHAT safe, specifically?

Peter
OSR
@OSRDrivers

This is the first time I’ve done this; yoyo’ed an IRP. It works, and I like
it, but something feels slightly uncomfortable about it. I can’t put my
finger on it. Also, I probably should change the IRP flags to indicate read
operation vs. write operation with IRP_READ_OPERATION and
IRP_WRITE_OPERATION. Although it seems redundant.

– Jamey

On Sun, Jun 25, 2017 at 9:16 AM wrote:

> I guess I don’t understand the question.
>
> Is WHAT safe, specifically?
>
> Peter
> OSR
> @OSRDrivers
>
>
> —
> NTDEV is sponsored by OSR
>
> Visit the list online at: <
> http://www.osronline.com/showlists.cfm?list=ntdev&gt;
>
> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
> software drivers!
> Details at http:
>
> To unsubscribe, visit the List Server section of OSR Online at <
> http://www.osronline.com/page.cfm?name=ListServer&gt;
></http:>

Why do you allocate a new MDL if you do not have a new buffer ?

The swapbuffer sample does this but with a new buffer allocated from non-paged pool.

H. G.

First I allocate new buffer from nonpaged pool (see the Write() functions),
then I create an MDL for it, and replace the MDL in the IRP. I guess I
could just swap out the buffer in the current MDL. I’d still have to mark
the MDL as using NPP. How would I unmark the MDL when I was ready to swap
back to the original buffer? It seems to me that creating a new MDL is the
easiest method for my NPP buffer. I don’t know the performance impedance of
creating a new MDL each time. I suspect it is nominal compared to the fact
that I am issuing a read before every write.)

If I were not creating a new buffer, my driver would be whacky for sure; on
every write, I’s be reading what was already on the disk and then writing
that back out. Not to mention I’d be overwriting a callers buffer on direct
IO. We could call it “Active Write Protection” :slight_smile:

On Sun, Jun 25, 2017 at 11:32 AM wrote:

> Why do you allocate a new MDL if you do not have a new buffer ?
>
>
>
> The swapbuffer sample does this but with a new buffer allocated from
> non-paged pool.
>
>
>
> H. G.
>
>
>
> —
> NTDEV is sponsored by OSR
>
> Visit the list online at: <
> http://www.osronline.com/showlists.cfm?list=ntdev&gt;
>
> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
> software drivers!
> Details at http:
>
> To unsubscribe, visit the List Server section of OSR Online at <
> http://www.osronline.com/page.cfm?name=ListServer&gt;
></http:>

>If an Irp is being completed at DISPATCH_LEVEL the underlying filters and the
volume manager driver will have a hard time to process this reused IRP

A storage IRP is completed at DISPATCH_LEVEL most of the time.