Scsi Disk设备驱动通过Scsi middle level层向scsi host提交请求,scsi disk driver是一个块设备驱动,其调用块设备层的unplug_fn函数处理scsi disk的请求队列。在处理scsi disk请求队列过程中会调用scsi middle level层注册的scsi_request_fn函数实现具体的操作。Scsi_request_fn函数会从io调度队列中获取一个请求,然后将该请求转换成scsi command,最后直接调用scsi_dispatch_cmd函数将scsi命令提交给scsi host。Scsi host与scsi middle level层的接口是queue_command函数,每个scsi host驱动都会向scsi middle level层注册具体的queue_command方法。由于Queue_command函数在不可睡眠的上下文中执行,所以其不能处理复杂的操作,通常的操作是将接收到的scsi命令放置到scsi host维护的处理队列中。如果一个真是的scsi host,而不是一个虚拟的host时,那么在scsi host层可以将scsi command通过DMA的方式传输到scsi disk。上述过程完成了一个io请求的提交过程,对于诸如磁盘这样的设备,在这一过程中需要考虑到存储介质的特性以及应用访问模式的特性,所以需要做一些IO调度的策略,使得scsi磁盘的读写更加满足存储介质的特性。当然也可以在scsi disk的上层实现更加高级的IO管理策略。IO请求的提交可以理解为整个IO过程的前半部,那么后半部就是IO完成的回调过程,下文分析Linux中IO回调路径的具体实现。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
当一个IO事件完成之后,scsi disk会采用中断的方式通知scsi host驱动。当scsi host的中断事件发生之后,CPU会执行host的中断服务程序,通常实际的scsi host都会以PCI设备的形式存在,考虑到中断共享问题,在中断服务程序中首先需要进行中断事件的判断,然后根据scsi host的状态寄存器进行具体中断任务的处理。对于读写IO请求,当数据DMA到scsi disk之后会产生DMA结束中断信号,在DMA过程中可以采用聚散DMA(scatter-gather DMA)的技术,因此这一过程不会涉及到数据的内存拷贝,也就是说在读写IO过程中,数据一直处于bio的Page页中(写数据过程中会直接将Page页中的数据DMA到磁盘,读数据过程中直接将磁盘中的数据DMA到bio的Page中,这样的处理机制效率较高)。当host确定完成请求之后,会调用scsi middle level的回调函数,该回调函数就是著名的scsi_done。Scsi_done在queue_command的过程中被提交到scsi host层。在scsi_done函数中直接调用了blk_complete_request函数,该函数通过raise_softirq_irqoff(BLOCK_SOFTIRQ)触发了scsi的软中断。到目前为止,上述过程都在scsi host的中断上半部中执行。中断上半部运行时间不能过长,否则会导致中断事件的丢失。触发软中断之后,中断上半部就可以退出了。在退出上半部之后,CPU将会交给已经触发的scsi软中断服务程序,此时可以看到软中断的服务程序仍然运行在中断上下文,并不是一个可以调度的context。
软中断的执行函数是blk_done_softirq,由于是scsi command引发的中断事件,因此会调用事先注册到请求队列上的scsi_softirq_done函数,完成具体的scsi软中断下半部事件处理。在该函数中会进行一些scsi command执行的正确性判断,如果命令执行错误,那么可以采用重试的方法进行命令的requeue处理,当重试到一定程度之后会将执行错误的scsi命令交给scsi错误处理内核守护进程,进行最后的判决;如果执行成功,那么调用scsi_finish_command函数结束掉scsi命令。在scsi_finish_command函数中调用scsi_io_completion函数结束块级的io request,具体会调用scsi_end_request函数,然后调用blk_end_request函数,最后调到blk_end_io函数。在blk_end_io函数中会结束掉request上的所有bio,结束bio的过程可以调用bio_endio函数。Request中的所有bio都结束之后释放request资源。至此,一个bio所在的request被scsi disk处理完毕之后,通过中断上半部和下半部已经全部处理完毕。这里需要注意的是,IO的所有回调过程都是在中断上下文中处理的,所以在编写IO的回调函数时需要注意睡眠问题,需要考虑内存分配可能带来的睡眠,信号量的使用会导致睡眠,从而使系统崩溃。
通过上述分析,scsi disk的正常IO回调路径涉及的函数描述如下:scsi_doneàblk_done_softirqàscsi_softirq_doneàscsi_finish_commandàscsi_io_completionàscsi_end_requestàblk_end_ioàbio_endio。
最近Linux在Scsi middle level方面变化比较大,上述的分析基于较新的Linux-<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2.6.28版本,和前面的2.6.18版本相比IO回调流程也发生了一定变化。
用户411565 2009-6-25 11:59
用户193005 2009-6-24 12:00
用户193005 2009-6-24 11:36