Книга: Writing Windows WDM Device Drivers
IRP Processing
Разделы на этой странице:
IRP Processing
I/O Request Packets (IRPs) are central to the operation of drivers. It is helpful to look at them briefly at this point, but they are described in full later. In particular, this section looks at how IRP parameters are stored and how the IRP stack lets an irp be processed by a stack of drivers.
An IRP is a kernel "object", a predefined data structure with a set of I/O Manager routines that operate on it. The I/O Manager receives an I/O request and then allocates and initializes an IRP before passing it to the highest driver in the appropriate driver stack.
An IRP has a fixed header part and a variable number of IRP stack location blocks, as shown in Figure 3.1. Each I/O request has a major function code (such as IRP_MJ_CREATE corresponding to a file open) and possibly a minor function code. For example, the IRP_MJ_PNP Plug and Play IRP has several minor functions (e.g., IRP_MN_START_DEVICE). Table 3.5 lists the common IRP major function codes.
Table 3.5 Common IRP Major Function Codes
IRP_MJ_CREATE |
Create or open device file |
IRP_MJ_CLOSE |
Close handle |
IRP_MJ_READ |
Read |
IRP_MJ_WRITE |
Write |
IRP_MJ_CLEANUP |
Cancel any pending IRPs on a file handle |
IRP_MJ_DEVICE_CONTROL |
Device I/O control |
IRP_MJ_INTERNAL_DEVICE_CONTROL This is also called IRP_MJ_SCSI |
Device I/O control from a higher driver |
IRP_MJ_SYSTEM_CONTROL |
Windows Management Instrumentation |
IRP_MJ_POWER |
Power Management request |
IRP_MJ_PNP |
Plug and Play message |
IRP_MJ_SHUTDOWN |
Shutdown notification |
The fixed part of an IRP (the IRP structure itself) contains fixed attributes of the IRP. Each stack location — an IO_STACK_LOCATION structure — in fact contains most of the pertinent IRP parameters.
More than one IRP stack location is used when an IRP might be processed by more than one driver. Each driver gets its irp parameters from the current IRP stack location. If you pass an irp down the stack of drivers for the current device, you must set up the next stack location with the correct parameters. The parameters that you pass down may be different from the ones you are working with.
Figure 3.1 IRP Overview
IRP Parameters
When a write I/O request is made into an IRP, the I/O Manager fills in the main IRP header and builds the first IRP stack location. For a write, the IRP header contains the user buffer information. If you use Buffered I/O, the IRP AssociatedIrp.SystemBuffer field contains a pointer to the nonpaged copy of the user's buffer. For Direct I/O, the IRP MdlAddress field has a pointer to the user buffer MDL.
The IRP stack location has the main write request parameters. The stack MajorFunction field has the IRP_MJ_WRITE major function code that indicates that a write has been requested. The Parameters.Write.Length field has the byte transfer count and Parameters.Write.ByteOffset has the file pointer. The stack also has other important fields that are described later.
As I said earlier, you have to set up the next stack location if you call another driver. This means that you can theoretically change the IRP major function code to something else. While this might work in some cases, it is generally not a good idea, as the parameters in the fixed part of the IRP might not be correct.
However, it might be appropriate to change the number of bytes to transfer and the file pointer. You might do this if you know that the lower driver can only handle short transfers. To handle a large transfer, you might, therefore, send the IRP down several times. Each transfer request you send down will be within the capabilities of the lower driver.
Processing IRPs in a Driver Stack
In practice, the I/O stack locations are not usually used to alter these fundamental IRP parameters. Instead, the IRP stack is normally used to let an IRP be processed by all the drivers in a device stack.
Figure 3.2 shows how an IRP might be processed by four drivers in the device stack. The first IRP arrives at the highest driver, Driver 1. This uses the function IoGetCurrentlrpStack-Location to obtain a pointer to the current stack location. The figure shows that this returns the topmost IRP stack location.
Driver 1 decides that it needs to pass the IRP down the stack for processing. The IRP might be a Power Management IRP that the lowest bus driver needs to see. Driver 1 might not do anything with this IRP, but it still needs to pass the IRP down the stack.
Driver 1 therefore sets up the stack location for the next driver. In many cases, it simply copies the current stack location to the next using the IoCopyCurrentlrpStackLocation-ToNext or IoSkipCurrentlrpStackLocation functions. If you need to alter the next stack location, then use IoGetNextlrpStackLocation to get a pointer to it.
Driver 1 then calls the next driver down the stack using the IoCallDriver function. The I/O Manager now changes the "current IRP stack location" pointer so that Driver 2 sees the second IRP stack location down (the one that Driver 1 set up for it). This process continues until the lowest driver, Driver 4, receives the IRP.
Driver 4 now processes the IRP. When it has finished with the IRP, Driver 4 calls IoCompleteRequest to indicate that it has finished processing the IRP. The IRP travels back up the device stack until it eventually pops out the top and is returned to the user.
Each of the drivers in the stack is given an opportunity to work with the IRP again as it travels up the stack, if they wish to do so. To do this, a driver must attach a completion routine to an IRP using the IoSetCompletionRoutine function. The completion routine information is stored in each driver's IRP stack location. This lets each of the drivers numbered 1 to 3 work on the IRP as it travels back up the stack in the order: Driver 3, Driver 2, and then Driver 1. A driver does not need to attach a completion routine. In this case, the I/O Manager does not call the driver as the IRP passes up the stack.
Figure 3.2 How all drivers in a stack process an IRP
A driver does not have to pass an IRP down the stack. If it detects an error in a parameter, or is able to process the IRP itself, then it should do its job and complete the IRP with IoCompleteRequest.
When a driver processes an IRP as it travels back up the device stack, it does not necessarily let the IRP progress further up the stack straightaway. If Driver 2 for example, splits Write IRPs into small chunks that Driver 3 can digest, then it may send the IRP back down the stack again, with changed parameters.
A further possibility is that a driver can build a new IRP and send it down the stack. Driver 2 might be processing a read request. To satisfy this request, it has to send an IOCTL request to the lower drivers. When the IOCTL request returns, Driver 2 checks that it worked satisfactorily and carries on processing its Read IRP.
To reiterate, an IRP includes a stack of I/O request operations. A driver only looks at the current IRP stack location and does not have to worry if there are higher-level drivers above.
- Power IRPs
- IRP Queuing
- Processing Commands
- Background Processing
- Apache Multiprocessing Modules
- ACID Compliance in Transaction Processing to Protect Data Integrity
- Конфигурирование базовой станции AirPort с помощью отличающегося от AirPort клиента
- 5.3. Kernel Command Line Processing
- 10.5 Processing General Exceptions
- 11.5.1 Possible Processing Delays
- 8.5.4 Processing and Printing Text
- Элемент xsl:processing-instruction