Wdm1Read could issue a kernel request that blocks its operation[14]. Windows 98 might then schedule a different user application that issues another read resulting in another call to Wdm1Read.

The first technique to make your routine reentrant is to use local variables. Each separate call to Wdm1Read definitely has its own separate set of variables on the kernel stack.

Do not use global variables or variables in the device extension unless you protect access to them. However, when these variables are first set up (DriverEntry or AddDevice), you do not need to take special precautions.

The Wdm1 driver uses a spin lock to protect access to the shared memory buffer variables, as described later.

There are two other main techniques to achieve reentrancy in dispatch routines. The first is to use the services of the I/O Manager to create a device queue of IRPs. The IRPs in the device queue are passed one at a time to a StartIo routine in your driver.

The second technique is to use Critical section routines if your device interrupts the computer, as explained in full in Chapter 16. Calling KeSynchronizeExecution runs your Critical section routine safely. KeSynchronizeExecution raises the IRQL to the interrupt IRQL and acquires the interrupt spin lock. This technique ensures that your routine is run to completion without being interrupted by another part of the driver.

IRP Handling

There are three main techniques for handling IRPs.

• Handle immediately

• Put in a queue and process one by one

• Pass down to a lower-level driver

Only the simplest drivers, like Wdm1, can handle IRPs straightaway. Even so, you must still take precautions to ensure that your driver dispatch routines are reentrant.

Drivers that access real pieces of hardware usually want to serialize access to the hardware. As mentioned previously, the I/O Manager in the kernel provides a device queue that you can use. Dispatch routines call the kernel to put an IRP into the device queue. The I/O Manager calls your StartIo routine to process one IRP at a time. When your StartIo routine has completed an IRP, it should call the kernel to ensure that it is called again with the next available IRP.

Drivers might want to use more than one device queue. For example, a serial port driver will usually want to have two device queues; one for incoming read data and one for outgoing write data.

A driver can use a different queuing strategy. For example, the DebugPrint driver described in Chapter 14 lets only one read IRP be queued. Any further read IRPs are rejected while the first read IRP is outstanding.

The final main technique for handling IRPs is to pass them down for handling in a lower-level driver. This approach is common in WDM device drivers. Some drivers simply pass the handling of some IRPs to the next lower driver and forget about them. If you take a more active interest, you can inspect the results after all the lower-level drivers have handled the IRP. These techniques are covered later.

A variant on this theme is for a driver to build a new IRP (or IRPs), send it down to lower drivers, and process the results afterwards.

IRP Completion

When a driver has finished working on an IRP, it must tell the I/O Manager. This is called IRP completion. As this code snippet shows, you must set a couple of fields in the IRP IoStatus field structure. IoStatus.Status is set to an NTSTATUS status code. The number of bytes transferred is usually stored in IoStatus.Information.

Irp->IoStatus.Status = STATUS_SUCCESS;

Irp->IoStatus.Information = BytesTxd;

IoCompleteRequest(Irp,IO_NO_INCREMENT);

Finally, IoCompleteRequest is called (at or below DISPATCH_LEVEL IRQL). As well as the IRP pointer, you must supply a PriorityBoost parameter to give a boost to the scheduling priority of the thread that originated the IRP. For example, keyboard drivers use the constant IO_KEYBOARD_INCREMENT. This is a high value of 6 because foreground threads should respond quickly to user input.

If a dispatch routine does not process an IRP immediately, it must mark the IRP as pending using IoMarkIrpPending and return STATUS_PENDING. When you eventually get round to completing the IRP, do it as described above.

If an IRP is queued and its associated process dies unexpectedly (or calls CancelIo to cancel overlapped IRPs), these pending IRPs must be cancelled. You must set an IRP's cancel routine to handle this circumstance, and to handle the Cleanup IRP. Chapter 16 describes these options in full. Wdm1 does not queue IRPs, so handling these options is not necessary.

IRP Structure

Figure 7.1 shows that an I/O Request Packet consists of a header IRP structure followed by a series of stack locations, each an IO_STACK_LOCATION structure. The information in the header and the current IRP stack location tell a driver what to do.

Figure 7.1 IRP overview

Table 7.2 lists some of the fields in the IRP header structure that a driver can access, while Table 7.3 gives the general layout of the IO_STACK_LOCATION structure.

Table 7.2 Some IRP structure fields

Field Description
IO_STATUS_BLOCK IoStatus Completion status of IRP
PVOID AssociatedIrp.SystemBuffer System space buffer (for Buffered I/O)
PMDL MdlAddress Memory Description List (for Direct I/O)
BOOLEAN Cancel Set if IRP has been cancelled
ULONG Flags IRP Flags

Table 7.3 Some IO_STACK_LOCATION structure fields

typedef struct _IO_STACK_LOCATION {

 UCHAR MajorFunction;

 UCHAR MinorFunction;

 // …

 union {

  struct { … } Create;

  struct { … } Read;

  struct { … } Write;

  struct { … } DeviceIoControl;

 } Parameters;

 // …

} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

The fact that there is an IRP header and several associated I/O stack locations can be a source of confusion. However, the stack locations are a powerful tool for processing IRPs when several layered drivers have to access an IRP in turn.

The I/O stack location contains most of the important information about the IRP. The MajorFunction is the IRP code (e.g., IRP_MJ_READ for a Read IRP). Some IRPs, such as IRP_ MJ_PNP, use the MinorFunction field to specify which particular Plug and Play function is being requested.

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

0

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

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