Support IRP cancellation

svn path=/trunk/; revision=30066
This commit is contained in:
Hervé Poussineau 2007-11-02 12:36:50 +00:00
parent 42bab9d044
commit 426db02b5b
4 changed files with 207 additions and 139 deletions

View file

@ -19,6 +19,12 @@ static DRIVER_DISPATCH ClassDeviceControl;
static DRIVER_DISPATCH IrpStub;
static DRIVER_ADD_DEVICE ClassAddDevice;
static DRIVER_STARTIO ClassStartIo;
static DRIVER_CANCEL ClassCancelRoutine;
static NTSTATUS
HandleReadIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
BOOLEAN IsInStartIo);
static VOID NTAPI
DriverUnload(IN PDRIVER_OBJECT DriverObject)
@ -82,8 +88,14 @@ ClassRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
NTSTATUS Status;
DPRINT("IRP_MJ_READ\n");
ASSERT(DeviceExtension->Common.IsClassDO);
if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
return ForwardIrpAndForget(DeviceObject, Irp);
@ -96,9 +108,10 @@ ClassRead(
return STATUS_BUFFER_TOO_SMALL;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
Status = HandleReadIrp(DeviceObject, Irp, FALSE);
KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
return Status;
}
static NTSTATUS NTAPI
@ -370,7 +383,6 @@ cleanup:
InitializeListHead(&DeviceExtension->ListHead);
KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
KeInitializeSpinLock(&DeviceExtension->SpinLock);
DeviceExtension->ReadIsPending = FALSE;
DeviceExtension->InputCount = 0;
DeviceExtension->PortData = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(KEYBOARD_INPUT_DATA), CLASS_TAG);
if (!DeviceExtension->PortData)
@ -454,56 +466,16 @@ ClassCallback(
IN OUT PULONG ConsumedCount)
{
PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
PIRP Irp = NULL;
KIRQL OldIrql;
SIZE_T InputCount = DataEnd - DataStart;
SIZE_T ReadSize;
DPRINT("ClassCallback()\n");
ASSERT(ClassDeviceExtension->Common.IsClassDO);
KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
DPRINT("ClassCallback()\n");
/* A filter driver might have consumed all the data already; I'm
* not sure if they are supposed to move the packets when they
* consume them though.
*/
if (ClassDeviceExtension->ReadIsPending == TRUE && InputCount)
{
/* A read request is waiting for input, so go straight to it */
NTSTATUS Status;
SIZE_T NumberOfEntries;
Irp = ClassDeviceObject->CurrentIrp;
ClassDeviceObject->CurrentIrp = NULL;
NumberOfEntries = MIN(
InputCount,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(KEYBOARD_INPUT_DATA));
Status = FillEntries(
ClassDeviceObject,
Irp,
DataStart,
NumberOfEntries);
if (NT_SUCCESS(Status))
{
/* Go to next packet and complete this request with STATUS_SUCCESS */
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA);
ClassDeviceExtension->ReadIsPending = FALSE;
/* Skip the packet we just sent away */
DataStart += NumberOfEntries;
(*ConsumedCount) += (ULONG)NumberOfEntries;
InputCount -= NumberOfEntries;
}
}
/* If we have data from the port driver and a higher service to send the data to */
if (InputCount != 0)
if (InputCount > 0)
{
if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
{
@ -529,20 +501,13 @@ ClassCallback(
ClassDeviceExtension->InputCount += ReadSize;
(*ConsumedCount) += (ULONG)ReadSize;
}
else
{
DPRINT("ClassCallback(): no more data to process\n");
}
/* Complete pending IRP (if any) */
if (ClassDeviceExtension->PendingIrp)
HandleReadIrp(ClassDeviceObject, ClassDeviceExtension->PendingIrp, FALSE);
}
KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
if (Irp != NULL)
{
IoStartNextPacket(ClassDeviceObject, FALSE);
IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
}
DPRINT("Leaving ClassCallback()\n");
return TRUE;
}
@ -693,6 +658,7 @@ ClassAddDevice(
DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
goto cleanup;
}
IoSetStartIoAttributes(Fdo, TRUE, TRUE);
DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
RtlZeroMemory(DeviceExtension, sizeof(PORT_DEVICE_EXTENSION));
@ -753,26 +719,61 @@ cleanup:
}
static VOID NTAPI
ClassStartIo(
ClassCancelRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLASS_DEVICE_EXTENSION ClassDeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
BOOLEAN wasQueued = FALSE;
DPRINT("ClassCancelRoutine(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(ClassDeviceExtension->Common.IsClassDO);
KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
IoAcquireCancelSpinLock(&OldIrql);
if (ClassDeviceExtension->PendingIrp == Irp)
{
ClassDeviceExtension->PendingIrp = NULL;
wasQueued = TRUE;
}
KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
if (wasQueued)
{
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
/* Hm, this shouldn't happen */
ASSERT(FALSE);
}
}
static NTSTATUS
HandleReadIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
BOOLEAN IsInStartIo)
{
PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
NTSTATUS Status;
DPRINT("HandleReadIrp(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(DeviceExtension->Common.IsClassDO);
if (DeviceExtension->InputCount > 0)
{
KIRQL oldIrql;
NTSTATUS Status;
SIZE_T NumberOfEntries;
NumberOfEntries = MIN(
DeviceExtension->InputCount,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(KEYBOARD_INPUT_DATA));
KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
Status = FillEntries(
DeviceObject,
Irp,
@ -790,22 +791,55 @@ ClassStartIo(
}
DeviceExtension->InputCount -= NumberOfEntries;
DeviceExtension->ReadIsPending = FALSE;
Irp->IoStatus.Information = NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA);
}
/* Go to next packet and complete this request */
Irp->IoStatus.Status = Status;
KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
if (IsInStartIo)
IoStartNextPacket(DeviceObject, TRUE);
(VOID)IoSetCancelRoutine(Irp, NULL);
IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
DeviceExtension->PendingIrp = NULL;
}
else
{
DeviceExtension->ReadIsPending = TRUE;
(VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine);
if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
{
DeviceExtension->PendingIrp = NULL;
Status = STATUS_CANCELLED;
}
else
{
IoMarkIrpPending(Irp);
DeviceExtension->PendingIrp = Irp;
Status = STATUS_PENDING;
if (!IsInStartIo)
IoStartPacket(DeviceObject, Irp, NULL, NULL);
}
}
return Status;
}
static VOID NTAPI
ClassStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
DPRINT("ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(DeviceExtension->Common.IsClassDO);
KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
HandleReadIrp(DeviceObject, Irp, TRUE);
KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
}
static VOID NTAPI

View file

@ -59,7 +59,7 @@ typedef struct _CLASS_DEVICE_EXTENSION
LIST_ENTRY ListHead;
KSPIN_LOCK ListSpinLock;
KSPIN_LOCK SpinLock;
BOOLEAN ReadIsPending;
PIRP PendingIrp;
SIZE_T InputCount;
PKEYBOARD_INPUT_DATA PortData;
LPCWSTR DeviceName;

View file

@ -19,6 +19,12 @@ static DRIVER_DISPATCH ClassDeviceControl;
static DRIVER_DISPATCH IrpStub;
static DRIVER_ADD_DEVICE ClassAddDevice;
static DRIVER_STARTIO ClassStartIo;
static DRIVER_CANCEL ClassCancelRoutine;
static NTSTATUS
HandleReadIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
BOOLEAN IsInStartIo);
static VOID NTAPI
DriverUnload(IN PDRIVER_OBJECT DriverObject)
@ -82,8 +88,14 @@ ClassRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
NTSTATUS Status;
DPRINT("IRP_MJ_READ\n");
ASSERT(DeviceExtension->Common.IsClassDO);
if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
return ForwardIrpAndForget(DeviceObject, Irp);
@ -96,9 +108,10 @@ ClassRead(
return STATUS_BUFFER_TOO_SMALL;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
Status = HandleReadIrp(DeviceObject, Irp, FALSE);
KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
return Status;
}
static NTSTATUS NTAPI
@ -347,9 +360,8 @@ cleanup:
InitializeListHead(&DeviceExtension->ListHead);
KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
KeInitializeSpinLock(&DeviceExtension->SpinLock);
DeviceExtension->ReadIsPending = FALSE;
DeviceExtension->InputCount = 0;
DeviceExtension->PortData = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(KEYBOARD_INPUT_DATA), CLASS_TAG);
DeviceExtension->PortData = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(MOUSE_INPUT_DATA), CLASS_TAG);
if (!DeviceExtension->PortData)
{
ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG);
@ -430,56 +442,16 @@ ClassCallback(
IN OUT PULONG ConsumedCount)
{
PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
PIRP Irp = NULL;
KIRQL OldIrql;
SIZE_T InputCount = DataEnd - DataStart;
SIZE_T ReadSize;
DPRINT("ClassCallback()\n");
ASSERT(ClassDeviceExtension->Common.IsClassDO);
KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
DPRINT("ClassCallback()\n");
/* A filter driver might have consumed all the data already; I'm
* not sure if they are supposed to move the packets when they
* consume them though.
*/
if (ClassDeviceExtension->ReadIsPending == TRUE && InputCount)
{
/* A read request is waiting for input, so go straight to it */
NTSTATUS Status;
SIZE_T NumberOfEntries;
Irp = ClassDeviceObject->CurrentIrp;
ClassDeviceObject->CurrentIrp = NULL;
NumberOfEntries = MIN(
InputCount,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(MOUSE_INPUT_DATA));
Status = FillEntries(
ClassDeviceObject,
Irp,
DataStart,
NumberOfEntries);
if (NT_SUCCESS(Status))
{
/* Go to next packet and complete this request with STATUS_SUCCESS */
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = NumberOfEntries * sizeof(MOUSE_INPUT_DATA);
ClassDeviceExtension->ReadIsPending = FALSE;
/* Skip the packet we just sent away */
DataStart += NumberOfEntries;
(*ConsumedCount) += (ULONG)NumberOfEntries;
InputCount -= NumberOfEntries;
}
}
/* If we have data from the port driver and a higher service to send the data to */
if (InputCount != 0)
if (InputCount > 0)
{
if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
{
@ -505,20 +477,13 @@ ClassCallback(
ClassDeviceExtension->InputCount += ReadSize;
(*ConsumedCount) += (ULONG)ReadSize;
}
else
{
DPRINT("ClassCallback(): no more data to process\n");
}
/* Complete pending IRP (if any) */
if (ClassDeviceExtension->PendingIrp)
HandleReadIrp(ClassDeviceObject, ClassDeviceExtension->PendingIrp, FALSE);
}
KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
if (Irp != NULL)
{
IoStartNextPacket(ClassDeviceObject, FALSE);
IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
}
DPRINT("Leaving ClassCallback()\n");
return TRUE;
}
@ -669,6 +634,7 @@ ClassAddDevice(
DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
goto cleanup;
}
IoSetStartIoAttributes(Fdo, TRUE, TRUE);
DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
RtlZeroMemory(DeviceExtension, sizeof(PORT_DEVICE_EXTENSION));
@ -729,26 +695,61 @@ cleanup:
}
static VOID NTAPI
ClassStartIo(
ClassCancelRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLASS_DEVICE_EXTENSION ClassDeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
BOOLEAN wasQueued = FALSE;
DPRINT("ClassCancelRoutine(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(ClassDeviceExtension->Common.IsClassDO);
KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
IoAcquireCancelSpinLock(&OldIrql);
if (ClassDeviceExtension->PendingIrp == Irp)
{
ClassDeviceExtension->PendingIrp = NULL;
wasQueued = TRUE;
}
KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
if (wasQueued)
{
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
/* Hm, this shouldn't happen */
ASSERT(FALSE);
}
}
static NTSTATUS
HandleReadIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
BOOLEAN IsInStartIo)
{
PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
NTSTATUS Status;
DPRINT("HandleReadIrp(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(DeviceExtension->Common.IsClassDO);
if (DeviceExtension->InputCount > 0)
{
KIRQL oldIrql;
NTSTATUS Status;
SIZE_T NumberOfEntries;
NumberOfEntries = MIN(
DeviceExtension->InputCount,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(MOUSE_INPUT_DATA));
KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
Status = FillEntries(
DeviceObject,
Irp,
@ -766,22 +767,55 @@ ClassStartIo(
}
DeviceExtension->InputCount -= NumberOfEntries;
DeviceExtension->ReadIsPending = FALSE;
Irp->IoStatus.Information = NumberOfEntries * sizeof(MOUSE_INPUT_DATA);
}
/* Go to next packet and complete this request */
Irp->IoStatus.Status = Status;
KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
if (IsInStartIo)
IoStartNextPacket(DeviceObject, TRUE);
(VOID)IoSetCancelRoutine(Irp, NULL);
IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
DeviceExtension->PendingIrp = NULL;
}
else
{
DeviceExtension->ReadIsPending = TRUE;
(VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine);
if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
{
DeviceExtension->PendingIrp = NULL;
Status = STATUS_CANCELLED;
}
else
{
IoMarkIrpPending(Irp);
DeviceExtension->PendingIrp = Irp;
Status = STATUS_PENDING;
if (!IsInStartIo)
IoStartPacket(DeviceObject, Irp, NULL, NULL);
}
}
return Status;
}
static VOID NTAPI
ClassStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
DPRINT("ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(DeviceExtension->Common.IsClassDO);
KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
HandleReadIrp(DeviceObject, Irp, TRUE);
KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
}
static VOID NTAPI

View file

@ -59,7 +59,7 @@ typedef struct _CLASS_DEVICE_EXTENSION
LIST_ENTRY ListHead;
KSPIN_LOCK ListSpinLock;
KSPIN_LOCK SpinLock;
BOOLEAN ReadIsPending;
PIRP PendingIrp;
SIZE_T InputCount;
PMOUSE_INPUT_DATA PortData;
LPCWSTR DeviceName;