If you give each queued IRP a cancel routine, most normal cases are covered (i.e., the afore-mentioned Cases 1 and 2). To be thorough, however, you ought to provide a Cleanup handler to cover Case 3. As you well know, programmers are bound to forget to close a file handle sometime or other.

If you do not provide a cancel routine for IRPs but do provide a Cleanup handler, only Case 3 is handled correctly. This is an unsatisfactory solution. In addition, user mode applications will not be able to cancel IRPs with CancelIo.

Some drivers work by providing a cancel routine only for an IRP while it is queued. Once the IRP actually begins processing, the cancel routine is removed. If you use this technique, be sure to provide a reasonable timeout for real IRP processing. Otherwise, crashed programs will not be able to exit.

DebugPrint IRP Cancelling

Full Cancel and Cleanup support can be quite complicated. This is particularly the case for IRPs put in the device queue for processing in a StartIo routine. A full example for this case is given in Chapter 16.

The DebugPrint driver uses just a cancel routine for its one queued IRP. It does not handle the Cleanup IRP.

The DbpCancelIrp routine shown in Listing 14.8 is called if an IRP is cancelled. An I/O request is cancelled when a user mode application calls the Win32 CancelIO function. This technique is used by the DebugPrint Monitor application. The kernel will also cancel any outstanding IRPs if a process terminates unexpectedly or when the file handle is closed.

The I/O Manager uses its Cancel spin lock to ensure that cancel operations happen safely.

A cancel routine is always called at DISPATCH_LEVEL IRQL holding this Cancel spin lock. The DbpCancelIrp routine can simply release this straightaway.

DbpCancelIrp then goes on to acquire the DebugPrint device extension ReadIrpLock spin lock. It checks to see if the given IRP pointer matches the one in the queue. If it does, it clears the queue. Regardless of whether the given IRP matches the one in the list, DbpCancelIrp just cancels the IRP by calling CompleteIrp, passing a status of STATUS_CANCELLED.

Listing 14.8 DebugPrint driver cancel routine

VOID DbpCancelIrp(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {

 PDEBUG_PRINT_DEVICE_EXTENSION dx = (PDEBUGPRINT_DEVICE_EXTENSION)fdo->DeviceExtension;

 IoReleaseCancelSpinLock(Irp->CancelIrql);

 // If this is our queued read, then unqueue it

 KIRQL irql ;

 KeAcquireSpinLock(&dx->ReadIrpLock,&irql);

 if (Irp==dx->ReadIrp) {

  UnlockDevice(dx);

  dx->ReadIrp = NULL;

 }

 KeReleaseSpinLock(&dx->ReadIrpLock,irql);

 // Whatever Irp it is, just cancel it

 CompleteIrp(Irp, STATUS_CANCELLED, 0);

}

Write Algorithm

The DebugPrint driver write routine DbpWrite, shown in Listing 14.9, at first sight looks pretty similar to the DebugPrintMsg routine described earlier. Its job is to insert the trace event data into an interlocked doubly-linked list. If there is a Read IRP queued up, DbpWrite goes on to satisfy this read request.

DbpWrite first gets the write parameters, and completes Write IRPs with a zero transfer length straightaway. The device file pointer is ignored.

As shown previously, the event list is stored in the device extension in field EventList, protected by spin lock EventListLock. Each event is stored in a DEBUGPRINT_EVENT structure.

DbpWrite determines the correct size for the DEBUGPRINT_EVENT structure and tries to allocate some memory for it from the nonpaged pool. It fails the IRP with STATUS_INSUFFICIENT_RESOURCES if no memory is available. Next, it copies the event data into the event and stores the data length, before calling ExInterlockedInsertTailList in the same way as before to insert the event safely into EventList.

DbpWrite now checks to see if there is a queued Read IRP. It must first grab the ReadIrpLock spin lock. If the ReadIrp field is not NULL, ReadEvent is called to complete the Read IRP and the ReadIrp field is reset to NULL.

Finally, DbpWrite completes its own Write IRP, returning STATUS_SUCCESS.

Listing 14.9 DebugPrint driver write routine

NTSTATUS DbpWrite(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {

 PDEBUGPRINT_DEVICE_EXTENSION dx = (PDEBUGPRINT_DEVICE_EXTENSION)fdo->DeviceExtension;

 if (!dx->IODisabled) return CompleteIrp(Irp, STATUS_DEVICE_NOT_CONNECTED, 0);

 if (!LockDevice(dx)) return CompleteIrp(Irp, STATUS_DELETE_PENDING, 0);

 PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);

 ULONG BytesTxd = 0;

 // Check write len

 ULONG WriteLen = IrpStack->Parameters.Write.Length;

 if (WriteLen==0) {

  UnlockDevice(dx);

  return CompleteIrp(Irp, STATUS_SUCCESS, 0);

 }

 // Copy write data into an event

 ULONG Len = sizeof(LIST_ENTRY)+sizeof(ULONG)+WriteLen;

 PDEBUGPRINT_EVENT pEvent = (PDEBUGPRINT_EVENT)ExAllocatePool(NonPagedPool,Len);

 if (pEvent==NULL) {

  UnlockDevice(dx);

  return CompleteIrp(Irp,STATUS_INSUFFICIENT_RESOURCES,0);

 }

 pEvent->Len = WriteLen;

 RtlCopyMemory(pEvent->EventData, Irp->AssociatedIrp.SystemBuffer, WriteLen);

 // Insert event into event list

 ExInterlockedInsertTailList(&dx->EventList,&pEvent-> ListEntry,&dx->EventListLock);

 // If read pending, then read it

 KIRQL irql;

 KeAcquireSpinLock(&dx->ReadIrpLock,&irql);

 if (dx->ReadIrp!=NULL) if (ReadEvent(dx, dx->ReadIrp)) {

  UnlockDevice(dx);

  dx->ReadIrp = NULL;

 }

 KeReleaseSpinLock(&dx->ReadIrpLock,irql);

 // Complete IRP

 UnlockDevice(dx);

 return CompleteIrp(Irp, STATUS_SUCCESS, WriteLen);

}

Read Algorithm

The ReadEvent routine shown in Listing 14.10 is called by the read and write dispatch routines. It is called while holding the ReadIrpLock spin lock. ReadEvent returns true if a trace event was found.

ReadEvent tries to remove an entry from the event list using ExInterlockedRemoveHeadList. If it finds an entry, it obtains a pointer to the DEBUGPRINT_EVENT structure using CONTAINING_RECORD. It now checks the event data length against the size of the Read IRP buffer and shortens the transfer count, if necessary. The event data is copied to the Read I/O buffer and the Read IRP is completed. Finally, the event buffer memory is freed.

Listing 14.10 DebugPrint driver ReadEvent routine

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×