Splitting I/O requests

I’m writing a device driver that needs to split up I/O requests. Right now
it creates a virtual disk device. It then remaps the reads and writes to a
partition without a drive letter. I want to split every sector to be read
into a new IRP. I’m having trouble getting it to work though. It doesn’t
crash and the reads and writes seem to go through OK. But anything that
tries to access the volume runs really slow and doesn’t seem to get the
right data from it. Does any one have any ideas? Is there an example
somewhere of splitting up an IRP into multiple IRPs and sending them to
another device? I would think file system and fault tolerant disk drivers
would have to do this.

I do the following in my dispatch:

case IRP_MJ_READ:
case IRP_MJ_WRITE:

// Mark the original IRP as pending
IoMarkIrpPending (Irp) ;

// Get the length, offset and address
length = irpStack->Parameters.Read.Length ;
byteOffset = RtlLargeIntegerAdd (devExt->StartByte,
irpStack->Parameters.Read.ByteOffset) ;

multiplexInfo = (PIRP_MULTIPLEX_INFO)ExAllocatePool (NonPagedPool,
sizeof(IRP_MULTIPLEX_INFO)) ;
if (multiplexInfo == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

multiplexInfo->MasterIrp = Irp ;

multiplexInfo->TotalAssociatedIrps = length / 512 + ((length % 512) ? 1
: 0) ;

multiplexInfo->IrpCount = multiplexInfo->TotalAssociatedIrps ;

multiplexInfo->AssociatedIrps = (PIRP *)ExAllocatePool (NonPagedPool,
sizeof(PIRP)*multiplexInfo->TotalAssociatedIrps) ;

if (multiplexInfo->AssociatedIrps == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

i = 0 ;
currentSubIrpAddressOffset = 0 ;
while (length)
{
// PIRP subIrp ;
ULONG currentSubIrpLength ;

// Check for buffer overrun
ASSERT (i < multiplexInfo->TotalAssociatedIrps) ;

currentSubIrpLength = 512 ;
if (length < 512)
{
currentSubIrpLength = length ;
}

multiplexInfo->AssociatedIrps[i] = BuildAssociatedReadWriteIrp
(Irp,
devExt->DiskDevice,
&byteOffset,
currentSubIrpLength,
multiplexInfo,
currentSubIrpAddressOffset) ;
if (multiplexInfo->AssociatedIrps[i] == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES ;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

IoSetCompletionRoutine (multiplexInfo->AssociatedIrps[i],
VfDiskReadWriteCompletionRoutine,
multiplexInfo, // Pass in the original IRP as the context for the
completion routine.
TRUE,
TRUE,
TRUE) ;

byteOffset = liAdd (byteOffset, li(currentSubIrpLength)) ;
currentSubIrpAddressOffset += currentSubIrpLength ;
length -= currentSubIrpLength ;
i++ ;
}

ASSERT (i == multiplexInfo->TotalAssociatedIrps) ;

// Call the disk driver

for (i = 0; i < multiplexInfo->TotalAssociatedIrps; i++)
{
status = IoCallDriver (devExt->DiskDevice,
multiplexInfo->AssociatedIrps[i]) ;
}

return STATUS_PENDING ;

.
.
.

BuildAssociatedReadWriteIrp() is a function I created:

PIRP BuildAssociatedReadWriteIrp (IN PIRP MasterIrp,
IN PDEVICE_OBJECT DeviceToCall,
IN PLARGE_INTEGER ByteOffset,
IN ULONG Length,
IN PIRP_MULTIPLEX_INFO multiplexInfo,
IN ULONG OffsetIntoOriginalMdl)
{
PIRP newIrp ;
PIO_STACK_LOCATION currentStackLocation ;
PIO_STACK_LOCATION firstStackLocation ;
PMDL mdl = NULL ;
PVOID NewIrpOffset ;
LOCK_OPERATION lockOperation ;

// Make an associated new IRP
newIrp = IoAllocateIrp (DeviceToCall->StackSize + 1,
FALSE) ;

if (!newIrp)
{
return NULL ;
}

// Make sure the stack is set up right
IoSetNextIrpStackLocation (newIrp) ;

// Get the stack location for the device to call
firstStackLocation = IoGetNextIrpStackLocation (newIrp) ;

// Get our stack location in the master IRP
currentStackLocation = IoGetCurrentIrpStackLocation (MasterIrp) ;

// Duplicate the parameters
firstStackLocation->Parameters.Read.ByteOffset = *ByteOffset ;
firstStackLocation->Parameters.Read.Key =
currentStackLocation->Parameters.Read.Key ;
firstStackLocation->Parameters.Read.Length = Length ;
firstStackLocation->MajorFunction = currentStackLocation->MajorFunction ;
firstStackLocation->MinorFunction = currentStackLocation->MinorFunction ;

mdl = IoAllocateMdl (MmGetMdlVirtualAddress(MasterIrp->MdlAddress),
MmGetMdlByteCount(MasterIrp->MdlAddress),
FALSE,
FALSE,
newIrp) ;

if (!mdl)
{
IoFreeIrp (newIrp) ;
return NULL ;
}

NewIrpOffset = (PUCHAR)MmGetMdlVirtualAddress(MasterIrp->MdlAddress) +
OffsetIntoOriginalMdl ;

IoBuildPartialMdl (MasterIrp->MdlAddress,
mdl,
NewIrpOffset,
Length) ;
return newIrp ;
}

In my completion routine I do this:

NTSTATUS VfDiskReadWriteCompletionRoutine (IN PDEVICE_OBJECT deviceObject,
IN PIRP Irp,
IN PVOID context)
{
NTSTATUS status ;
PIRP_MULTIPLEX_INFO multiplexInfo ;
ULONG information ;
PIRP masterIrp ;
BOOLEAN bLastIrp ;
KIRQL oldIrql ;

multiplexInfo = (PIRP_MULTIPLEX_INFO)context ;
masterIrp = multiplexInfo->MasterIrp ;
bLastIrp = FALSE ;

ASSERT (multiplexInfo->IrpCount > 0) ;

status = Irp->IoStatus.Status ;
information = Irp->IoStatus.Information ;

ASSERT (KeGetCurrentIrql() <= DISPATCH_LEVEL) ;
KeRaiseIrql (DISPATCH_LEVEL, &oldIrql) ;

multiplexInfo->IrpCount-- ;
if (multiplexInfo->IrpCount == 0)
{
bLastIrp = TRUE ;
}

KeLowerIrql (oldIrql) ;

if (bLastIrp)
{
LONG i ;

for (i = 0; i < multiplexInfo->TotalAssociatedIrps; i++)
{
PIRP irpToFree ;

irpToFree = multiplexInfo->AssociatedIrps[i] ;

// unlock & free mdls
while (irpToFree->MdlAddress != NULL)
{
PMDL nextMdl;
nextMdl = irpToFree->MdlAddress->Next;
// MmUnlockPages(irpToFree->MdlAddress);
IoFreeMdl(irpToFree->MdlAddress);
irpToFree->MdlAddress = nextMdl;
}

IoFreeIrp (irpToFree) ;
}

ExFreePool (multiplexInfo->AssociatedIrps) ;
ExFreePool (multiplexInfo) ;
multiplexInfo = NULL ;
COMPLETE_REQUEST ( masterIrp, status, information );
}

return STATUS_MORE_PROCESSING_REQUIRED;
}


You are currently subscribed to ntdev as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com