BLOCK层代码分析(5)IO下发之BIO bounce过程
BLOCK层代码分析(7)IO下发之request的分配和获取
BLOCK层代码分析(8)IO下发之plug/unplug机制
更新了BLOCK层代码分析(1)(2)后,觉得有必要对存储IO框架层次做介绍,因此增加了本小节。
1. 存储IO框架
存储IO内核框架如下图所示:
以上列出几种驱动的存储软件框架,包含NVME驱动/SAS驱动/SATA驱动。IO依次经过系统调用层、文件系统层、BLOCK层、SCSI层、LIBSAS/LIBATA层以及LLDD。但并不是所有的这些层都经过,比如对于NVME驱动,对接BLOCK层,并不会经过SCSI层等。并且上图只是简单的描述,每层又可以细分,这里不做详细描述。
对于每一层,作用描述如下:
- 系统调用:处理系统调用;
- 文件系统层:建立硬盘和内存数据的映射,存储和组织数据,以便查询和访问;
- BLOCK层:下发/完成文件系统传输过来的数据,同时可能做合并/切分和排序(IO调度层);
- SCSI层:下发/完成数据,对SCSI硬盘进行识别;
- LIBSAS/LIBATA层:下发/完成数据,识别设备拓扑;
- LLDD: 下发/完成数据,对特定的硬件进行适配;
上面只是对每一层的功能做最简单的介绍,后面有机会会分别对每层做详细的描述。
2. IO发送和完成过程简述
可以看出从BLOCK到LLDD层,每层都需要下发和完成IO。对于每一层,在下发时都会注册相应的完成函数,当完成时就会调用对应的完成函数一层层向上传递(这里描述的是正常完成过程,除了正常完成过程还存在异常完成即超时机制(如果在N秒内,IO没有完成,就会进入超时流程处理IO完成))。
如上图为hisi_sas驱动的发送完成过程的简化(实际在下发过程中还有SAS盘和SATA盘不同路径下发和完成,这里暂时忽略),block层暂时不介绍,后续章节会重点介绍block层。
SCSI层使用函数scsi_queue_rq()下发IO时注册对应的完成函数scsi_done()。LIBSAS层使用函数sas_queuecommand()下发IO时注册对应的完成函数sas_scsi_task_done()。底层驱动使用hisi_sas_queue_command()下发IO时注册对应的完成函数slot_complete_vx_hw()。
当硬件完成数据的处理后,一般会产生中断通知软件开始完成过程。软件依次会调用slot_complete_vx_hw()-> sas_scsi_task_done() -> scsi_done()完成IO,此过程一般会放在中断下半部执行。当然并不是所有的完成都是通过中断通知软件的,也可以软件进行轮询硬件是否完成(当前IO_URING支持poll)。
3. IO在各层的表示
IO在每层使用不同的结构体来表示,下图hisi_sas驱动所涉及的各层IO的结构体表示,以及相互之间的关系。
BLOCK层处理之前提到的bio外,主要使用request。SCSI层使用scsi command。LIBATA层使用ata_queued_cmd。LIBSAS使用sas_task。hisi_sas驱动使用hisi_sas_slot。
NOTE:本节以及后面章节的分析都是基于当前最新内核(比如目前为5.16-rc1),内核变动可能会有小的差异。另相应可能涉及的代码在内核中的位置为:
BLOCK层代码:block/
SCSI层代码: drivers/scsi/
LIBSAS层代码:drivers/scsi/libsas/
hisi_sas代码:drivers/scsi/hisi_sas/