I/O Request Packet (IRP)
An I/O Request Packet (IRP) is a data structure that the I/O Manager uses to manage I/O operations. It contains all the information necessary to perform an I/O operation, from the time a user-mode application initiates the request to the time the request is completed by a device driver.
Structure of an IRP
An IRP is a complex structure that can be thought of as a container for I/O-related information. Key components include:
- Major and Minor Function Codes: These codes specify the type of I/O operation to be performed (e.g., read, write, device control) and its specific variant.
- Stack Location: Each driver in the I/O path has a stack location within the IRP. This location contains parameters specific to that driver's handling of the request.
- I/O Status Block: This block holds the status of the I/O operation (success or failure) and any information returned by the operation.
- Associated Device Object: A pointer to the target device object for the I/O request.
- User Buffer: If the operation involves transferring data, this points to the user-provided buffer.
- Cancel Routine: A pointer to a routine that can be called to cancel the IRP.
IRP Stack Location
Each driver that processes an IRP is given a "stack location" within the IRP. This stack location contains:
- MajorFunction: The major I/O function code for the current driver.
- MinorFunction: The minor I/O function code for the current driver.
- Parameters: A union that holds parameters specific to the major function code. For example, a read or write operation would have parameters for the buffer and length, while a device control operation would have parameters for the control code and input/output buffers.
- DeviceObject: A pointer to the device object that the current driver is processing.
- FileObject: A pointer to the file object associated with the request.
- CompletionRoutine: A pointer to a driver-defined routine that is called when the IRP has been processed by lower drivers.
- Context: Driver-defined context information.
Common IRP Major Function Codes
The following table lists some of the most common IRP major function codes:
| Code | Name | Description |
|---|---|---|
| IRP_MJ_CREATE | Create | Creates a new file or opens an existing one. |
| IRP_MJ_READ | Read | Reads data from a device. |
| IRP_MJ_WRITE | Write | Writes data to a device. |
| IRP_MJ_DEVICE_CONTROL | Device Control | Performs device-specific operations. |
| IRP_MJ_CLOSE | Close | Closes a file or handle. |
| IRP_MJ_CLEANUP | Cleanup | Performs cleanup operations before closing a file. |
| IRP_MJ_PNP | Plug and Play | Handles Plug and Play events. |
| IRP_MJ_POWER | Power Management | Handles power management events. |
IRP Processing Flow
- An application or system component initiates an I/O request.
- The I/O Manager creates an IRP and populates its initial stack location for the top-most driver.
- The I/O Manager passes the IRP to the driver's dispatch routine.
- The driver processes the request, potentially performs some work, and then passes the IRP down to the next driver in the stack using
IoCallDriver. - This continues until the lowest driver in the stack completes the operation.
- As the IRP returns up the stack, each driver can perform post-processing or call its completion routine.
- Finally, the I/O Manager marks the IRP as completed and informs the requesting application.
Creating and Managing IRPs
Kernel-mode drivers use functions provided by the I/O Manager to create, allocate, and manage IRPs. Key functions include:
IoAllocateIrp: Allocates an IRP.IoReuseIrp: Reuses a previously allocated IRP.IoFreeIrp: Frees an IRP.IoSetCompletionRoutine: Sets a completion routine for an IRP.
PIRP AllocateAndInitializeIrp(
IN CCHAR StackSize
)
{
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
Irp = IoAllocateIrp(StackSize, NULL);
if (Irp == NULL) {
return NULL;
}
IrpSp = IoGetNextIrpStackLocation(Irp);
// Initialize Stack Location parameters here...
// IrpSp->MajorFunction = IRP_MJ_READ;
// IrpSp->Parameters.Read.Length = ...;
// IrpSp->Parameters.Read.Key = ...;
// IrpSp->Parameters.Read.ByteOffset = ...;
return Irp;
}
Canceling IRPs
Drivers can implement cancellation logic to abort I/O operations that are in progress. This is typically done by setting a CancelRoutine in the IRP's stack location and calling IoSetCancelRoutine.
Understanding IRPs is fundamental to developing robust and efficient device drivers in the Windows kernel.