SCSI层的IO大体上分为两种,一种为从BLOCK下发的IO称为普通IO(通常数据的读写为此类),另一种为从当前SCSI下发的IO,如前面介绍的扫盘IO,称为passthrough IO。本节分别介绍这两种IO的下发和完成。
1. SCSI层数据的组织
在BLOCK层的数据是由bio表示,其中bi_sectors表示数据在硬盘中的位置和长度,而结构体bio_vec[]表示数据在内存中的位置和长度(见BLOCK层代码分析(1)数据的组织BIO)。
到BLOCK层数据的组织由SCSI命令和SGL聚散链表共同决定。其中SCSI命令指明数据在硬盘中的位置和长度,SGL聚散链表则指明数据在内存中的位置和长度。
SCSI命令READ和WRITE定义如下,其中LBA表示数据在硬盘中位置,transfer length表示为数据的长度。
SGL聚散链表表示数据在内存中的位置和长度,详细见BLOCK层代码分析(6)之SGL聚散列表。
2. 普通IO的下发和完成
从BLOCK层下发的IO依次经过SCSI层/LIBSAS层/LLDD层,最终将IO下发给硬件,在各层下发的同时会注册IO完成函数。在硬件处理完数据后,一般会产生中断通知LLDD层(当然也可以轮询方式,如IO_URING就是采用轮询),然后依次调用LLDD层/LIBSAS层/SCSI层的IO完成函数,将IO完成返回到上层。简单过程如下:
3. passthrough IO的下发和完成
除了从BLOCK层下发的IO外,在SCSI层还存在”内部IO”,即从SCSI层下发的,硬盘的识别过程中下发的IO如INQUIRY等,都是这种IO,称为passthrough IO.
对于passthrough IO, 在SCSI层会获取request和tag,若存在数据时,将数据进行映射,然后将数据经过BLOCK层而下发,后面的步骤与普通IO的下发和完成类似,只是最终IO会在SCSI完成,而不会像普通IO那样传递给BLOCK层。
Passthrough IO下发过程和完成过程如下:
(1)通过函数blk_get_request()获取空闲的request;
(2)若IO存在数据时,分配bio,将数据map到bio中(若原来数据不对齐或bounce,需要分配page,必要时并将数据拷贝到page),并将bio添加到request;
(3)然后将request下发到IO 调度器中;
(4)若为poll方式,轮询等待IO完成;若非poll方式,等待IO完成;
(5)将IO返回结果和sense返回,完成IO;