Hi all
Thanks for your comments, I just wrote and posted a lengthy reply and explanation but it seems to have disappeared… not my week…
The short version of the earlier reply is that yes, it turns out we are calling IoBuildDeviceIoControlRequest to issue an abort and reset from our thread at irql =0 . This only happens on the rare occasion an unplug causes the xact error instead of the device removed error (as detected in the completion routine). This is because in this case, the call to iocalldriver (in the thread) returns STATUS_UNSUCCESSFUL instead of STATUS_NO_SUCH_DEVICE which leads us to think we can abort/reset and retry. The abort succeeds but the IoCallDriver for the reset fails and we panic and don’t complete the irp.
Bingo! This is the bug that must lead to the uncompleted irp that I can’t find in windbg.
The abort/reset code has been around a long time and I haven’t been focussed on looking for problems in that area. If I complete the irp after IoCallDriver fails, then first indications are that it is now working.
However, I don’t understand why calling the irp complete function doesn’t result in the completion routine running if the IoCallDriver fails.
Thanks again for you help , it was the witch hunt for IoBuildDeviceIoControlRequest that eventually uncovered the problem with our SendAwaitUrb function.
Sean
ps This is the function used to send the urb… failing IoCallDriver completes the irp but doesn’t hang around for the completion routine.
NTSTATUS SendAwaitUrbOnComplete(PDEVICE_OBJECT pdo, PIRP Irp, PVOID pev)
{
KeSetEvent((PKEVENT) pev, IO_NO_INCREMENT, FALSE);
if(!NT_SUCCESS(Irp->IoStatus.Status))
{
ser_print(“SendAwaitUrbOnComplete failed %lx\r\n”,Irp->IoStatus.Status);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS SendAwaitUrb(IN PDEVICE_OBJECT LowerDeviceObject, PURB urb)
{
KEVENT event;
IO_STATUS_BLOCK iostatus={0};
PIRP Irp;
PIO_STACK_LOCATION stack;
NTSTATUS ntStatus;
KeInitializeEvent(&event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB, LowerDeviceObject, NULL, 0, NULL, 0, TRUE, &event, &iostatus);
if (!Irp)
{
ser_string(“Unable to allocate IRP for sending URB\n”);
return STATUS_INSUFFICIENT_RESOURCES;
}
IoSetCompletionRoutine(Irp, SendAwaitUrbOnComplete, (PVOID) &event, TRUE, TRUE, TRUE);
stack = IoGetNextIrpStackLocation(Irp);
ASSERT(stack != NULL);
stack->Parameters.Others.Argument1 = (PVOID) urb;
ntStatus = IoCallDriver(LowerDeviceObject, Irp);
if(!NT_SUCCESS(ntStatus))
{
ser_print(“IoCallDriver failed %lx\r\n”,ntStatus);
ser_print(“IoCallDriver failed ioStatus %lx\r\n”,iostatus.Status);
ser_print(“IoCallDriver failed urb->UrbHeader.Status %lx\r\n”,urb->UrbHeader.Status);
// KeClearEvent(&event);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
// ser_string(“SendAwaitUrb calling KeWaitForSingleObject\r\n”);
// ASSERT_KEWAIT_IRQL(0);
// KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
// ser_string(“SendAwaitUrb KeWaitForSingleObject returned\r\n”);
return ntStatus;
}
if (ntStatus == STATUS_PENDING)
{
LARGE_INTEGER Timeout;
Timeout.QuadPart = -1 * 10000000;
ASSERT_KEWAIT_IRQL(Timeout.QuadPart);
if ( KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &Timeout) == STATUS_TIMEOUT)
{
IoCancelIrp(Irp); // okay in this context
//ser_string(“SendAwaitUrb calling KeWaitForSingleObject\r\n”);
ASSERT_KEWAIT_IRQL(0);
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
//ser_string(“SendAwaitUrb KeWaitForSingleObject returned\r\n”);
ntStatus = STATUS_IO_TIMEOUT; // presumably we haven’t had an error as the irp hasn’t completed
}
else
{
ntStatus = iostatus.Status;
}
if (!NT_SUCCESS(ntStatus))
{
ser_print(“SendAwaitUrb KeWaitForSingleObject iostatus 0x%08lx\r\n”,ntStatus);
}
}
KeClearEvent(&event);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ser_string(“SendAwaitUrb calling KeWaitForSingleObject\r\n”);
ASSERT_KEWAIT_IRQL(0);
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
ser_string(“SendAwaitUrb KeWaitForSingleObject returned\r\n”);
return ntStatus;
}