Monday 23 January 2012

The I/O Request Packet

Structure of an IRP

The I/O Manager creates this MDL for IRP_MJ_READ and IRP_MJ_WRITE requests if the topmost device object’s flags indicate DO_DIRECT_IO.


Flags (ULONG) contains flags that a device driver can read but not directly alter. None of these flags are relevant to a Windows Driver Model (WDM) driver.

AssociatedIrp (union) is a union of three possible pointers. The alternative that a typical WDM driver might want to access is named AssociatedIrp.SystemBuffer. The SystemBuffer pointer holds the address of a data buffer in nonpaged kernel-mode memory.

RequestorMode will equal one of the enumeration constants UserMode or KernelMode,

Map of the Tail union in an IRP

I/O stack location data structure

The “Standard Model” for IRP Processing

Creating an IRP:
You can use any of four functions to create a new IRP:

  • IoBuildAsynchronousFsdRequest builds an IRP on whose completion you don’t plan to wait. This function and the next are appropriate for building only certain types of IRP.
  • IoBuildSynchronousFsdRequest builds an IRP on whose completion you do plan to wait.
  • IoBuildDeviceIoControlRequest builds a synchronous IRP_MJ_DE­VICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL request.
  • IoAllocateIrp builds an asynchronous IRP of any type

Creating Synchronous IRPs
1. If the owning thread terminates, the I/O Manager automatically cancels any pending synchronous IRPs that belong to that thread
2. Because the creating thread owns a synchronous IRP, you shouldn’t create one in an arbitrary thread—you most emphatically do not want the I/O Manager to cancel the IRP because this thread happens to terminate
3. Following a call to IoCompleteRequest, the I/O Manager automatically cleans up a synchronous IRP and signals an event that you must provide.
4. You must take care that the event object still exists at the time the I/O Manager signals it

You must call IoBuildSynchronousFsdRequest  and IoBuildDeviceIoControlRequest  functions at PASSIVE_LEVEL only

If you need to synchronize IRPs sent to another driver, consider the following alternatives:

  • Use a regular kernel mutex instead of an executive fast mutex. The regular mutex leaves you at PASSIVE_LEVEL and doesn’t inhibit special kernel APCs.
  • Use KeEnterCriticalRegion to inhibit all but special kernel APCs, and then use ExAcquireFastMutexUnsafe to acquire the mutex.
  • Use an asynchronous IRP. Signal an event in the completion routine.
A final consideration in calling the two synchronous IRP routines is that you can’t create just any kind of IRP using these routines. See Table 5-1 for the details. A common trick for creating another kind of synchronous IRP is to ask for an IRP_MJ_SHUTDOWN, which has no parameters, and then alter the MajorFunction code in the first stack location.
Table 5-1. Synchronous IRP Types
Support Function
Types of IRP You Can Create
IoBuildSynchronousFsdRequest
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_SHUTDOWN
IRP_MJ_PNP
IRP_MJ_POWER (but only for IRP_MN_POWER_SEQUENCE)
IoBuildDeviceIoControlRequest
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL


Creating Asynchronous IRPs

The other two IRP creation functions—IoBuildAsynchronousFsdRequest and IoAllocateIrp—create an asynchronous IRP. Asynchronous IRPs don’t belong to the creating thread, and the I/O Manager doesn’t schedule an APC and doesn’t clean up when the IRP completes

  • When a thread terminates, the I/O Manager doesn’t try to cancel any asynchronous IRPs that you happen to have created in that thread.
  • It’s OK to create asynchronous IRPs in an arbitrary or nonarbitrary thread.
  • Because the I/O Manager doesn’t do any cleanup when the IRP completes, you must provide a completion routine that will release buffers and call IoFreeIrp to release the memory used by the IRP.
  • Because the I/O Manager doesn’t automatically cancel asynchronous IRPs, you might have to provide code to do that when you no longer want the operation to occur
  • Because you don’t wait for an asynchronous IRP to complete, you can create and send one at IRQL <= DISPATCH_LEVEL (assuming, that is, that the driver to which you send the IRP can handle the IRP at elevated IRQL—you must check the specifications for that driver!). Furthermore, it’s OK to create and send an asynchronous IRP while owning a fast mutex
Refer to Table 5-2 for a list of the types of IRP you can create using the two asynchronous IRP routines. Note that IoBuildSynchronousFsdRequest and IoBuildAsynchronousFsdRequest support the same IRP major function codes.

Table 5-2. Asynchronous IRP Types
Support Function
Types of IRP You Can Create
IoBuildAsynchronousFsdRequest
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_SHUTDOWN
IRP_MJ_PNP
IRP_MJ_POWER (but only for IRP_MN_POWER_SEQUENCE)
IoAllocateIrp
Any (but you must initialize the MajorFunction field of the first stack location)


Forwarding to a Dispatch Routine
After you create an IRP, you call IoGetNextIrpStackLocation to obtain a pointer to the first stack location. Then you initialize just that first location. If you’ve used IoAllocateIrp to create the IRP, you need to fill in at least the MajorFunction code. If you’ve used another of the four IRP-creation functions, the I/O Manager might have already done the required initialization. You might then be able to skip this step, depending on the rules for that particular type of IRP. Having initialized the stack, you call IoCallDriver to send the IRP to a device driver

The I/O Manager initializes the stack location pointer in the IRP to 1 before the actual first location.

Locating Device Objects
IoGetDeviceObjectPointer, 


The StartIo Routine
A StartIo routine generally receives control at DISPATCH_LEVEL, meaning that it must not generate any page faults.

    No comments:

    Post a Comment