ggggchen
驱动牛犊
驱动牛犊
  • 注册日期2005-11-27
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分16分
  • 威望42点
  • 贡献值0点
  • 好评度1点
  • 原创分0分
  • 专家分0分
阅读:2777回复:0

reactos storage driver analyze

楼主#
更多 发布于:2010-07-29 17:08

React OS SCSI driver Analyze
Rev 0.2
Rev 0.1    2009-8-4    gang
Rev 0.2    2009-10-13    gang
        

Disk Driver --- Driver Entry
The first story is about disk driverentry. Here we meet a important structure. and a function

1)    CLASS_INIT_DATA
2)    ScsiClassInitialize

Here it seems our mainly job is to initialize the structure. And the function ScsiClassInitialize to register the structure to system.

The below routines have some variable and function pointer.  From the function pointers we could knows that the function pointer is class specific routine in CLASS_INIT_DATA. In fact in the kernel only SCSI related using CLASS_INIT_DATA refer to structure CLASS_INIT_DATA 1.  We know scsi device have many device types. The class driver in the kernel can match to the concept that scsi device type.

For each scsi type device there are specific routines, it is the function pointers in CLASS_INIT_DATA. Also all the scsi type device may have common jobs. They reflect in the ScsiClassInitialize function refer to function part of ScsiClassInitialize 1.  In the function ScsiClassInitialize it also have hook below function pointers which is common routine for different type devices. Please refer to Reference common class scsi irp handlers routines 1

In the function ScsiClassInitialize the InitializationData->ClassFindDevices routine will be called to find scsi device which is belong to the specific class. At this time, the lower layer driver such as scsi port driver is already ready.

Here we research for disks device the implement function is FindScsiDisks

FindScsiDisks function will scan all the disks on the port driver and register to class layer driver also it would initialize the structures there. In FindScsiDisks function routine it would initialize the DEVICE_EXTENSION structure structure _DEVICE_EXTENSION 2



The important members list below
 
•    deviceExtension->ClassError          
•    deviceExtension->ClassReadWriteVerification
•    deviceExtension->ClassFindDevices        
•    deviceExtension->ClassDeviceControl        
•    deviceExtension->ClassShutdownFlush        
•    deviceExtension->ClassCreateClose          
•    deviceExtension->ClassStartIo              
•    deviceExtension->MediaChangeCount;



Scsi send down work flows
DriverEntry already worked finish. Now let’s start to study the storages drivers how to handle commands. Before said that, Here we didn’t talked about the IRP related knowledges. The Firgures 1 shows a global pictures Figure IRP transfer relation picture 1.

The disk class driver mainly receive below 3 types IRPS, others IRPs mainly focus on tuning purpose.
1.    IRP_MJ_READ
2.    IRP_MJ_WRITE
3.    IRP_MJ_SCSI

ScsiClassReadWrite(scsi class)
And IRP_MJ_READ and IRP_MJ_WRITE irps are handled by ScsiClassReadWrite function. This function have below jobs to do:

This is the system entry point for read and write requests. The device-specific handler is invoked                              
    to perform any validation necessary. The number of bytes in the request are
    checked against the maximum byte counts that the adapter supports and requests are broken up into
    smaller sizes if necessary.

Besides it also to create scsi srb for the read write IRP , set the IRP’s next stacklocation to IRP_MJ_SCSI and turn send the IRP down.

Below list the functions ScsiClassReadWrite would call
    deviceExtension->ClassReadWriteVerification
    ScsiClassSplitRequest
    ScsiClassBuildRequest
    IoCallDriver

ClassReadWriteVerfication function is a device binding verification function. The device which bellowing to a scsi class should implement this function. The purpose here to call this function is easily to know that verify the IRP before continue handle.

ScsiClassSplitRequest function is used to split the IRP to a number of sub IRPs if necessary.

ScsiClassBuildRequest function create and set Scsi Request Block associated with the IRP and prepare the nect stack location of the IRP. SRB is need by scsiport driver which only handle scsi IRPs.

IoCallDriver send the IRP down to scsiport driver.

So do a summary, at class driver layer. When it receive a read/write IRP. It verify the IRP and prepare a SRB and translate as a scsi read write command and send it to port driver.

The main flow in disk class driver is finish below would discuss with scsiport driver.


ScsiPortDispatchScsi(scsi port)
Here we already in scsi port driver. As Figure IRP transfer relation picture 1 shows. In scsi port driver it mainly receive the scsi command. the normal write and read irp already convert to scsi read write command and transfer to scsi port driver.

The scsi command would handled by function ScsiPortDispatchScsi as Reference scsiport driver irp handlers routines 2 shows.

ScsiPortDispatchScsi function mainly jobs
    Get LunExtension from Srb which associated with the IRP
    Handle srb command by function. The Srb can be divided to several classes by functions.
1)    flush and shutdown irps
2)    scsi command and io control irps
3)    Device claim and release irps
4)    queue of device control irps
The first 2 classes will send down and the 3) and 4) two class irps would handle in this function.

So from here we knows that scsi port driver would manager queue and device. It would not give the right of controlling queue and device to scsi miniport driver.

ScsiPortStartIo function is the scsiport driver's DriverStartIo function pointer. Next we will start to research this function.


ScsiPortStartIo(scsi port)
ScsiPortStartIo would do some preparation such as get various resources and then call ScsiPortStartPacket function to handle the currentIRP of device extension.

Below list the main works:

1)    Set Srb Flag depend on deviceExtension->SrbFlags value
2)    Get SrbInfo and SrbExtension for the Srb
3)    Add DeviceExtension request handle count
4)    Allocate AdapterChannel to the scsiport if necessary.
5)    Call ScsiPortStartPacket to handle device current IRP means to SRB.
So the ScsiPortStartIo major job is to prepare resource for the Srb.

ScsiPortStartPacket(scsi port)
ScsiPortStartPacket is the last function in scsi port driver before pass the scsi request block to scsi miniport driver. What it do list below:
1)    Ored device with status SCSI_PORT_RESET_REQUEST if the device’s current status’s is SCSI_PORT_RESET and request also comes.
2)    Reject Abort command if the command already not in LunExtension list.
3)    Set the request’s timer and insert the request to LunExtension to record the request before pass it to miniport driver’s HwStartIo routine.
4)    Pend a device DPC routine if needed depend on DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED

At here the OS responsibility is finish for sending request.

Below we would start to talk about srb finish process.

Scsi request finish process
ScsiPortNotification(scsi port)
ScsiPortNotification for complete request only append complete srb to the device’s interrupt data structure. And the SpiSaveInterruptData and ScsiPortDpcForIsr would handle the complete request.
SpiSaveInterruptData(scsi port)
SpiSaveInterruptData function do below jobs on complete srb list.
1)    clear device extension flag.
2)    If a srb return from scsi miniport driver have error set get sense mark, if necessary.
3)    If the get sense srb failed from scsi miniport driver, set sense failed.
ScsiPortDpcForIsr(scsi port)
ScsiPortDpcForIsr would get and remove srbinfo from complete srb list and complete them by call function SpiProcessCompletedRequest.

SpiProcessCompletedRequest(scsi port)
This function mainly do below things
1)    modify srb->databuffer to virtual address from system address
2)    flush data memory from cache
3)    remove srbInfo and srbExtension if necessary.
4)    call IoCompleteRequest on the IRP and return it to scsi class layer driver.
5)    Also there also do some status of lun or device extension change depend on the request.
6)    And also do some error handle depend on the request response. Such as get sense , check condition, busy, etc.

From here the request return back to Class driver IRP complete routine.

ScsiClassIoComplete(scsi class)
This function is the complete function of IRP in scsi class layer. It would do below things.
1)    It get srb from IRP
2)    It return srb structure into a lookaside memory pool for the device.
3)    it copy status from srb to IRP.
4)    It also do some error handling if the srb status is not succeed.
5)    Because the IRP is sub irp if the irp be splitted.  The function also check whether all sub irp returned if so, it would complete the original IRP. It also modify the original irp status depend on sub irp.
So ScsiClassReadWrite function may split a large IRP to multi subl IRPs and the ScsiClassIoComplete would check all sub IRPs and return the large IRP to upper driver.





Appendix
References
Reference common class scsi irp handlers routines 1
    DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiClassCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose;
    DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiClassReadWrite;
    DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassInternalIoControl;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiClassDeviceControlDispatch;
    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ScsiClassShutdownFlush;
    DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ScsiClassShutdownFlush;


Reference scsiport driver irp handlers routines 2
    /* Set handlers */
    DriverObject->DriverStartIo = ScsiPortStartIo;
    DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPortCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPortCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPortDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ScsiPortDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiPortDispatchScsi;
Structures
structure CLASS_INIT_DATA 1
typedef struct _CLASS_INIT_DATA {

    //
    // This structure size - version checking.
    //

    ULONG InitializationDataSize;

    //
    // Bytes needed by the class driver
    // for it's extension.
    //

    ULONG DeviceExtensionSize;

    DEVICE_TYPE DeviceType;

    //
    // Device Characteristics flags
    //  eg.:
    //
    //  FILE_REMOVABLE_MEDIA
    //  FILE_READ_ONLY_DEVICE
    //  FILE_FLOPPY_DISKETTE
    //  FILE_WRITE_ONCE_MEDIA
    //  FILE_REMOTE_DEVICE
    //  FILE_DEVICE_IS_MOUNTED
    //  FILE_VIRTUAL_VOLUME
    //

    ULONG DeviceCharacteristics;

    //
    // Device-specific driver routines
    //

    PCLASS_ERROR           ClassError;
    PCLASS_READ_WRITE      ClassReadWriteVerification;
    PCLASS_DEVICE_CALLBACK ClassFindDeviceCallBack;
    PCLASS_FIND_DEVICES    ClassFindDevices;
    PCLASS_DEVICE_CONTROL  ClassDeviceControl;
    PCLASS_SHUTDOWN_FLUSH  ClassShutdownFlush;
    PCLASS_CREATE_CLOSE    ClassCreateClose;
    PDRIVER_STARTIO        ClassStartIo;

} CLASS_INIT_DATA, *PCLASS_INIT_DATA;


structure _DEVICE_EXTENSION 2

typedef struct _DEVICE_EXTENSION
{
  PDEVICE_OBJECT DeviceObject;
  PDEVICE_OBJECT PortDeviceObject;
  LARGE_INTEGER PartitionLength;
  LARGE_INTEGER StartingOffset;
  ULONG DMByteSkew;
  ULONG DMSkew;
  BOOLEAN DMActive;
  PCLASS_ERROR ClassError;
  PCLASS_READ_WRITE ClassReadWriteVerification;
  PCLASS_FIND_DEVICES ClassFindDevices;
  PCLASS_DEVICE_CONTROL ClassDeviceControl;
  PCLASS_SHUTDOWN_FLUSH ClassShutdownFlush;
  PCLASS_CREATE_CLOSE ClassCreateClose;
  PDRIVER_STARTIO ClassStartIo;
  PIO_SCSI_CAPABILITIES PortCapabilities;
  PDISK_GEOMETRY DiskGeometry;
  PDEVICE_OBJECT PhysicalDevice;
  PSENSE_DATA SenseData;
  ULONG TimeOutValue;
  ULONG DeviceNumber;
  ULONG SrbFlags;
  ULONG ErrorCount;
  KSPIN_LOCK SplitRequestSpinLock;
  NPAGED_LOOKASIDE_LIST SrbLookasideListHead;
  LONG LockCount;
  UCHAR PortNumber;
  UCHAR PathId;
  UCHAR TargetId;
  UCHAR Lun;
  UCHAR SectorShift;
  UCHAR ReservedByte;
  USHORT DeviceFlags;
  PKEVENT MediaChangeEvent;
  HANDLE MediaChangeEventHandle;
  BOOLEAN MediaChangeNoMedia;
  ULONG MediaChangeCount;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

Functions


function part of ScsiClassInitialize 1

ULONG
ScsiClassInitialize(
    IN  PVOID            Argument1,
    IN  PVOID            Argument2,
    IN  PCLASS_INIT_DATA InitializationData
)
{
    ……ignore many lines……
      
  //
    // Update driver object with entry points.
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiClassCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose;
    DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiClassReadWrite;
    DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassInternalIoControl;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiClassDeviceControlDispatch;
    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ScsiClassShutdownFlush;
    DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ScsiClassShutdownFlush;

    if (InitializationData->ClassStartIo) {
        DriverObject->DriverStartIo = InitializationData->ClassStartIo;
    }

    //
    // Open port driver controller device objects by name.
    //

    do {

        sprintf(deviceNameBuffer, "\\Device\\ScsiPort%d", portNumber);

        DebugPrint((2, "ScsiClassInitialize: Open Port %s\n", deviceNameBuffer));

        RtlInitString(&deviceNameString, deviceNameBuffer);

        status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
                                              &deviceNameString,
                                              TRUE);

        if (!NT_SUCCESS(status)){
            break;
        }

        status = IoGetDeviceObjectPointer(&unicodeDeviceName,
                                           FILE_READ_ATTRIBUTES,
                                           &fileObject,
                                           &portDeviceObject);

        if (NT_SUCCESS(status)) {

            //
            // Call the device-specific driver's FindDevice routine.
            //

            if (InitializationData->ClassFindDevices(DriverObject, Argument2, InitializationData,
                                                     portDeviceObject, portNumber)) {

                deviceFound = TRUE;
            }
        }

        //
        // Check next SCSI adapter.
        //

        portNumber++;

    } while(NT_SUCCESS(status));

return deviceFound ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE;
}


Figures
Figure IRP transfer relation picture 1
 
附件名称/大小 下载次数 最后更新
windows_scsi_analyze.rar (17KB)  3 2010-07-29 17:10
游客

返回顶部