// Remove cancel routine

 KIRQL OldIrql;

 IoAcqiureCancelSpinLock(&OldIrql);

 IoSetCancelRoutine(Irp, NULL);

 IoReleaseCancelSpinLock(OldIrql);

 // Unlock device and complete

 IRP UnlockDevice(dx);

 CompleteIrp(Irp, dx->TxStatus, BytesTxd);

 IoStartNextPacket(fdo, TRUE);

 // Stop timer calls

 dx->StopTimer = true;

}

DPC Gotchas

Even with this simple DPC for deferred interrupt processing, there are some potential problems that you have to look out for.

The first point to note is that if two or more calls to IoRequestDpc are made before the DPC can be run, the DPC is only run once. Suppose two interrupts occur in quick succession. If each interrupt handler calls IoRequestDpc, you might expect that the DPC is run twice. However, if there is a long time before the DPC is run, then it is only run once. You must cope with this. In this situation, it might be that one interrupt wants a read call completed, while the next wants a write call completed. Use a separate flag in the device extension to indicate each condition. The DPC routine should be prepared to handle both situations. An easier alternative might be to use a different DPC routine for each condition.

For the WdmIo driver, this problem should almost never arise. The only situation I can envision is one in which an IRP is cancelled just after the interrupt handler calls IoRequestDpc.

However, in this case, the late-running DPC routine will find that the IRP has been cancelled, which is correct.

The other potential problems regarding DPCs will only occur in multiprocessor systems.

• A DPC routine is running on one processor as a device interrupt is handled on another.

• When an interrupt handler asks for a DPC to be run, it is run straight away on another processor before the interrupt service routine exits.

• Two or more DPC routines may be running at the same time on different processors.

The main solution to these problems is to use Critical Section routines whenever a DPC routine needs to access fields that an interrupt handler or another DPC use.

Custom DPCs

If you need to use more than one DPC, this is fairly straightforward. These 'custom DPCs' are also used for Custom Timers with fine grain time-outs.

Declare a KDPC object in nonpaged memory (e.g., in the device extension). Initialize it using KeInitializeDpc. The DeferredContext parameter is eventually passed to the custom DPC routine.

To ask that your DPC routine be run, call KeInsertQueueDpc from within your interrupt handler. KeInsertQueueDpc returns TRUE if the request was successfully queued. It returns FALSE if the DPC is already in the queue. The SystemArgument1 and SystemArgument2 parameters to KeInsertQueueDpc are eventually passed to your DPC routine. You can use KeRemoveQueueDpc to remove your DPC request from the queue.

Table 17.2 shows the function prototype for your custom DPC routine. There are three context parameters. To be compatible with the basic DPC handler, these should be your FDO, the IRP, and usually the device extension. However, use these as you wish.

Table 17.2 CustomDpc prototype

VOID CustomDpc (IRQL==DISPATCH_LEVEL)
Parameter Description
IN PKDPC Dpc DPC
IN PVOID Context DeferredContext parameter given to KeInitializeDpc
IN PVOID SystemArg1 SystemArgument1 passed to KeInsertQueueDpc (NULL for custom timers)
IN PVOID SystemArg2 SystemArgument2 passed to KeInsertQueueDpc (NULL for custom timers)

Timers

Two different types of timer can be used. A basic timer is called once every second; WdmIo uses this to detect device time-outs. Custom timers may be set up with resolutions starting from 100ns.

One-Second Interval Timers

The kernel provides easy access to a device timer that calls you back every second. The tinier must be initialized at PASSIVE_LEVEL IRQL using IoInitializeTimer. WdmIo calls IoInitializeTimer as follows in its AddDevice routine just after the FDO has been created. The final parameter to IoInitializeTimer is passed to the timer callback.

status = IoInitializeTimer(fdo, (PIO_TIMER_ROUTINE)Timeout1s, dx);

if (!NT_SUCCESS(status)) {

 IoDeleteDevice(fdo);

 return status;

}

Use IoStartTimer to start timer calls and IoStopTimer to stop them. Do not call IoStopTimer from within your timer routine. The first timer call may occur after less than one second.

The timer routine is called at DISPATCH_LEVEL. You will usually need to use a Critical Section routine if you wish to coordinate your activities with interrupt handling routines.

WdmIo Time-Outs

Some drivers start their timer calls when a device is created and stop them when it is removed. WdmIo reduces the number of timer callbacks. It starts the timer whenever an interrupt driven I/O starts. When the transfer is completed, it sets a StopTimer flag to indicate that its timer should be stopped. The next call to WdmIoStartIo checks this flag and calls IoStopTimer, if necessary. The timer is also stopped when the device is removed.

Listing 17.5 shows the WdmIo timer callback, Timeout1s, and the Critical Section routine that it uses, called Timeout1sSynch.

The device extension Timeout field serves two purposes. If –1, it indicates that no interrupt-driven transfer is in progress. The WdmIo code in DeviceIo.sss checks this value in several places to ensure that a read or write is indeed in progress. If Timeout is zero or more, it indicates the number of seconds left before the current transfer times out. The first timer callback may occur after less than one second, so the code that starts reads and writes adds one to the given time-out to be on the safe side.

Timeout1s, therefore, first checks whether there is a transfer in progress. If not (i.e., if Timeout is –1), it returns immediately. If the IRP Cancel flag is set, it calls the DPC routine directly. Otherwise, it calls Timeout1sSynch as a Critical Section routine. Timeout1sSynch checks and decrements Timeout. If Timeout has reached zero, the read or write must be stopped. Timeout is set to –1, the IRP return status is set appropriately and TRUE is returned.

If Timeout1sSynch returns TRUE, Timeout1s calls the DPC routine directly. This call to WdmIoDpcForIsr uses NULL

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

0

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

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