Notes on Windows SCSI Miniport Driver
Thanks to Microsoft, this article is a collection of articles from Microsoft authenticated documents
In the Microsoft® Windows® operating system, the SCSIport driver, in conjunction with vendor-written adapter-specific miniport drivers, was for many years, the only driver delivering SCSI commands to the storage targets. The SCSIport driver, however, was designed to work optimally with the parallel SCSI interconnects used with direct attached storage. It was neither designed to meet the high performance standards of Fibre Channel SAN configurations, nor to work well with hardware RAID.
As a consequence, organizations running mission critical Windows applications on their servers do not realize the maximum performance benefits or manageability of their Fibre Channel SANs or hardware RAID adapters (on both the host and storage arrays) when I/O passes between the host and storage target.
These limitations have been overcome with the development of Storport, the new device driver designed to supplement SCSIport on Windows Server 2003 and beyond. Storport is a new port driver that delivers higher I/O throughput performance, enhanced manageability, and an improved miniport interface. Together, these changes help hardware vendors realize their high performance interconnect goals.
SCSIport is the Microsoft® Windows® system-supplied storage port driver, designed to manage SCSI transport on parallel SCSI interconnects. During the StartIo routine, the SCSIport driver translates the I/O request packet into a SCSI request block (SRB) and queues it to the miniport driver, which decodes the SRB and translates it into the specific format required by the storage controller. The start I/O phase of the request, which includes build and start (see Figure 2), takes microseconds; hundreds of SRBs can be queued, ready to be processed before even a single I/O request is completed. In fact, the longest phase of the I/O request is the data seek time (latency); if the data is not in cache, finding the correct blocks on the physical disk can take several milliseconds. (Note that the diagram shows relative time, not actual time units.) Once the hardware processes the I/O request—that is, does the data transfer—the controller generates a hardware interrupt indicating that the I/O has been completed. The interrupt is, in turn, processed by the HwInterrupt miniport routine (indicated as ISR or Interrupt Service Routine in the diagrams), which receives the completed requests and begins the whole process again. Data transfers are performed by the hardware itself (using Direct Memory Access or DMA) without operating system intervention.
Limitations of SCSIPort
- SCSIport can support a maximum of 254 outstanding I/O requests per adapter.
- At any given time, the SCSIport driver supports either the issuing or the completion of I/O requests, but not simultaneous execution of both request functions. In other words, once an I/O request enters the StartIo routine and the SRB is sent to a host bus adapter (HBA), this transmission mode (sometimes rather misleadingly called “half duplex”) prevents the adapter from processing storage device interrupts until the start I/O processing phase is complete.
- The HwStartIo routine always executes with the interrupt request level (IRQL) of the processor at the same priority level as the interrupt request of the device. Because all interrupts with the same or lower priority are masked to enable a higher priority process to complete without interruption, the elevated IRQL means that hardware interrupts accumulate rather than being processed. With parallel SCSI adapters, this has minimal impact, since there is very little additional work for the miniport driver to do. However, when using Fibre Channel or hardware RAID adapters, the workload on the miniport driver is much heavier; as a consequence, considerably more time is spent at an elevated IRQL. The net result of high numbers of accumulated interrupts is degraded system performance.
- To correctly process an I/O request, the miniport driver must pass physical addresses that correspond to the IRP’s data buffer (called a scatter/gather list) to the adapter. The current architecture of the SCSIport driver requires that the miniport driver repeatedly call the port driver to access this information, one element at a time, rather than obtain a complete list all at once. This repeated calling is CPU and time-intensive, and becomes inefficient with large data transfers or when memory becomes fragmented.
SCSI Miniport Driver Routines
All SCSI miniport drivers must have some of the routines described in this section. Whether any particular miniport driver has others depends on the nature of its HBA(s) or on that miniport driver’s designer.
A miniport driver’s HwScsiXxx routines can have any name chosen by the driver writer. DriverEntry is a required name.
This section describes the following miniport driver routines:
DriverEntry of SCSI Miniport Driver
Each miniport driver must have a routine explicitly named DriverEntry in order to be loaded.
A miniport driver’s DriverEntry routine allocates memory on the stack and initializes a HW_INITIALIZATION_DATA structure with zeros. DriverEntry must zero all members in the HW_INITIALIZATION_DATA structure before initializing it with values appropriate to the HBA(s) the miniport driver supports.
Next, DriverEntry calls ScsiPortInitialize. If a miniport driver supports HBA(s) that can be connected on more than one type of I/O bus, such as both MicroChannel and Isa type buses, it should call ScsiPortInitialize once for each type of I/O bus. Such a miniport driver must return the lowest value returned by its calls to ScsiPortInitialize from the DriverEntry routine.
A miniport driver’s HwScsiAdapterControl routine is called to perform synchronous operations to control the state or behavior of an HBA, such as stopping or restarting the HBA for power management. It specifies one of the following adapter-control operations (listed important ones only).
HwScsiFindAdapter uses supplied configuration information to determine whether it supports a particular HBA and, if so, returns information about its HBA in the given ConfigInfo buffer.
For a legacy miniport driver, ScsiPortInitialize calls the HwScsiFindAdapter routine after allocating storage according to the DeviceExtensionSize that the miniport driver specified in the HW_INITIALIZATION_DATA structure. For a Plug and Play miniport driver, the port driver calls HwScsiFindAdapter when the Plug and Play manager has detected an adapter for that miniport driver.
HwScsiInitialize initializes the HBA after a reboot or a power failure occurs.
HwScsiInitialize must initialize the HBA but should avoid resetting the SCSI bus or buses if possible. Interrupts can occur before this routine is called. If HwScsiInitialize resets a bus, it must call ScsiPortNotification to report that the bus was reset.
HwScsiInterrupt is called when the HBA generates an interrupt. Miniport drivers of HBAs that do not generate interrupts do not have this routine
HwScsiResetBus resets a given SCSI bus.
HwScsiResetBus can be called even if the miniport driver is not ready for another request. The miniport driver should complete all pending requests and must reset the given bus.
HwScsiResetBus need not call ScsiPortNotification to indicate that the bus was reset.
All miniport drivers must have a HwScsiStartIo routine. The operating system-specific port driver calls HwScsiStartIo first with each incoming I/O request for a target on a SCSI bus.
As soon as it receives the initial request for a target peripheral, the operating system-specific port driver calls the HwScsiStartIo routine with an input SRB. After this call, the HBA miniport driver owns the request and is expected to complete it.
Subsequently, the operating system-specific port driver calls the HwScsiStartIo routine after the port driver receives each NextRequest, NextLuRequest, or notification as the miniport driver makes calls to ScsiPortNotification and/or ScsiPortCompleteRequest.
When the HwScsiStartIo routine is called but the driver needs to defer processing of the given SRB, HwScsiStartIo should do the following:
- Set the SrbStatus member to SRB_STATUS_BUSY.
- Call ScsiPortNotification with the request to be deferred and a notification type of RequestComplete.
- Return TRUE.
The port driver requeues such a request and resubmits it later.
HwScsiTimer is called after the interval specified when the miniport driver called ScsiPortNotification with the RequestTimerCall NotificationType value.
A miniport driver that does not have an HwScsiInterrupt routine because it manages all HBA I/O operations by polling should have an HwScsiTimer routine.
A miniport driver should use HwScsiTimer instead of ScsiPortStallExecution whenever a wait longer than a millisecond is required.
SCSI Port Library Routines
An operating system-specific SCSI port driver provides ScsiPortXxx routines to support operating system-independent SCSI miniport drivers that are linked with a Microsoft-supplied, operating system-specific SCSI port driver.
The SCSI port driver is a kernel-mode dynamic-link library.
ScsiPortInitialize stores the miniport driver’s initialization data for future use.
Every miniport driver’s DriverEntry routine must call ScsiPortInitialize after the miniport driver has first zeroed and then set up the HW_INITIALIZATION_DATA.
If a miniport driver can support HBAs on different types of I/O buses, such as both Isa and MicroChannel type I/O buses, the miniport driver should call ScsiPortInitialize for each supported interface type.
A miniport driver that calls ScsiPortInitialize more than once should check the value returned by ScsiPortInitialize at each call and save the lowest value for all its calls. The DriverEntry routine must return the lowest value when it returns control to the system. Miniport driver writers can make no assumptions about the values returned by ScsiPortInitialize.
ScsiPortCompleteRequest can be called to complete outstanding requests after a bus reset, a device reset, or an abort, rather than calling ScsiPortNotification for each outstanding request individually. After calling ScsiPortCompleteRequest, do not also call ScsiPortNotification.
The ScsiPortGetBusData routine returns bus-type-specific configuration information that a miniport driver’s HwScsiFindAdapter routine might use to determine whether it supports a particular adapter on a particular I/O bus, and to configure the HBA if it does.
The ScsiPortNotification routine informs the operating system-specific port driver of certain events, such as when a miniport driver completes a request or is ready to start another SRB, as well as when the HBA indicates certain SCSI error conditions that occurred during an operation.
|RequestComplete||Indicates the given Srb has finished. If this value is set, ScsiPortNotification requires one additional parameter: the address of the SRB. After this notification, the operating system-specific port driver owns the request. The miniport driver must not access the Srb, and it must not pass the Srb to another routine (such as ScsiPortLogError).|
|NextRequest||Indicates the miniport driver is ready for another request to a target that is not currently busy. This notification should be sent by the miniport driver as soon as the driver is ready for another request. Usually, this notification is sent from the HwScsiStartIo routine but, sometimes, from the HwScsiInterrupt (or HwScsiEnableInterruptsCallback) routine.|
|NextLuRequest||Indicates that the HBA is ready for another request for the specified logical unit. If this value is set, ScsiPortNotification requires three additional parameters: (1) the path ID, (2) the target ID, and (3) the logical unit number. This value should be used only if the HBA can queue multiple requests and support auto-request sense or tagged queuing.|
|ResetDetected||Indicates that the HBA has detected a reset on the SCSI bus. After this notification, the miniport driver is still responsible for completing any active requests. The SCSI port driver will manage all required bus-reset delays.|
|CallEnableInterrupts||Indicates that the miniport driver requires the operating system-specific port driver to call the miniport driver’s HwScsiEnableInterruptsCallback routine. If this value is set, ScsiPortNotification requires an additional parameter: the entry point for the HwScsiEnableInterruptsCallback. The miniport driver’s HwScsiInterrupt routine makes this call, after disabling interrupts on the HBA, to defer some interrupt-driven I/O processing if the HBA requires polling or stalling in the ISR. While the callback runs, system interrupts remain enabled but the miniport driver’s HwScsiInterrupt routine will not be called. The HwScsiEnableInterruptsCallback is responsible for completing the deferred I/O processing and for calling ScsiPortNotification again with CallDisableInterrupts and the miniport driver’s HwScsiDisableInterruptsCallback entry point.|
|CallDisableInterrupts||Indicates that the miniport driver requires the operating system-specific port driver to call the miniport driver’s HwScsiDisableInterruptsCallback routine. If this value is set, ScsiPortNotification requires an additional parameter: the entry point for the HwScsiDisableInterruptsCallback. While this callback runs, it cannot be preempted by an interrupt except from a device with a higher priority interrupt than the HBA. In this callback, the miniport driver reenables interrupts on the HBA.|
|RequestTimerCall||Indicates that the miniport driver requires the operating system-specific port driver to call the miniport driver’s HwScsiTimer routine in the requested number of microseconds. If this value is set, ScsiPortNotification requires two additional parameters: (1) the entry point for the miniport driver’s HwScsiTimer routine, and (2) a MiniportTimerValue interval, in microseconds. Note that the resolution of the system timer is approximately 10 milliseconds.|
|BusChangeDetected||Indicates that a target device might have been added or removed from a dynamic bus. If this value is set, ScsiPortNotification requires an additional parameter: the path ID of the bus on which the change was detected. After this notification, the port driver reenumerates the bus by issuing INQUIRY commands. Bus enumeration is time-consuming and ties up the bus, so a miniport driver should not send this notification unnecessarily.|
The ScsiPortWritePortUchar routine transfers an unsigned byte to the HBA.
The ScsiPortStallExecution routine stalls in the miniport driver.
Given the inherent limitations of using SCSIport with high performance adapters for which it was not designed, Microsoft has developed a new port driver, Storport. Storport has been architected as a replacement for SCSIport, and designed to enable realization of the high performance capabilities of hardware RAID and Fibre Channel adapters. It is possible for hardware vendors to write their own class, filter, or even new port drivers, to bypass SCSIport. But, unlike Storport, these drivers may perform unreliably with the Windows platform because they are designed without in-depth knowledge of the operating system.
While many of the routines in Storport are similar to SCSIport (which helps in a smooth transition from SCSIport to Storport), there are a number of critical differences. These differences, discussed in the remainder of this section, provide the advanced functionality of Storport that enables vendor miniport drivers and adapter hardware to function more effectively.
- Unlike SCSIport, which cannot process an interrupt while it is issuing an I/O (or start a new I/O while the device is interrupting), Storport introduces a new synchronization model that allows decoupling of the interrupt from the StartIo routine. This means that Storport can send and receive I/O requests simultaneously; I/O requests can be started at the same time that requests are being completed. Multiprocessor systems can make use of this synchronous (“full duplex”) I/O functioning, thereby accelerating data transfer, as is shown in figure.
- In Storport, a new routine called HwBuildIo has been added to handle much of this preparatory work before the command is sent to the hardware. The Storport HwBuildIo routine is designed to allow the miniport to do this build work at a lower IRQL (known as DISPATCH IRQL) than with SCSIport, and does it without the need for synchronization. Because this routine runs before the StartIo routine, and at a lower priority level, interrupts are enabled, allowing requests to be sent to the controller even as the controller processes other requests. The net result is faster I/O processing. Again, where necessary, synchronization can be forced by calling the StorPortSynchronizeAccess routine.
- One of the operations typically handled in the HwBuildIo routine is building the scatter/gather lists that identify the memory ranges and physical addresses where each portion of a data buffer resides in memory. Rather than making multiple calls to the port driver for each individual element (typically each element is a single memory page ), Storport can pass the entire scatter/gather list to the miniport in one call.
- Unlike SCSIport, which can only queue a maximum of 254 outstanding I/O requests to an adapter supporting multiple storage devices, Storport does not limit the number of outstanding I/O requests that can be sent to an adapter. Instead, each logical storage unit (such as a virtual disk on a RAID array or a physical disk drive) can accept up to 254 outstanding requests. The number of requests an adapter can handle is the number of logical units x 254.
- Improved Error and Reset Handling
- Hierarchical Resets
- Improved Clustering by Using Hierarchical Resets
- Fibre Channel Link Handling
- Ability to Run Deferred Procedure Calls (DPCs)
- Access to Windows Registry
- Fibre Channel Management
- Easy Migration to Storport
Migration from SCSIPort Miniport to StorPort Miniport
The Storport-miniport driver interface is designed to be as similar to the SCSI Port-miniport driver interface as possible, in order to facilitate the adaptation of SCSI Port miniport drivers to work with the Storport driver. In order to make a SCSI Port miniport driver work with Storport, you must take the following basic steps:
- Change all instances of the #include <scsi.h> directive with the #include <storport.h> directive.
If both the scsi.h and storport.h header files are included, a compile time error will occur.
- Replace scsiport.lib with storport.lib in your build scripts, that is, in the sources or makefile file.
- Make certain that all expanded structures are properly initialized.The sizes of both the HW_INITIALIZATION_DATA (SCSI) structure and the PORT_CONFIGURATION_INFORMATION (SCSI) structure have changed, so make certain the new members are properly initialized.
The Storport header file, storport.h, currently retains both SCSI Port-prefixed commands and StorPort-prefixed commands to facilitate porting from the SCSI Port.