原文地址:http://www.cnblogs.com/raymondpang/articles/4469500.html

1. 知识铺垫

  据我所知(可能并不准确),Linux的存储系统基本上都已经被整合到SCSI存储框架之下,SAS自不必说,像SATA接口的设备(机械硬盘、SSD、光驱),USB设备都已经由SCSI框架管理,而NVMe虽然还未完全融入SCSI存储框架(Kernel 3.16.0),但是已经预留了SCSI有关的接口,相信随着内核的更新,Intel一定会推动把NVMe也纳入到SCSI的管理之下。

  在阅读之前,应当提前了解的是:应用层的读写操作,会以bio的形式提交给block层,然后,block会把bio转换成request,并把该request加入到专为某个SCSI device服务的request queue中,接下来会有内核线程适时地从该queue中摘取request,并转换成SCSI cmd,再转换成ATA cmd,最终交由AHCI Controller(对于AHCI,实际上会把ATA cmd转换成FIS结构)发向硬盘执行。

  SCSI与ATA本就是两套不同的标准,因此它们各自的命令集是不同的,虽然有些功能类似的cmd,但是cmd格式还是不同的。

  本文主要关注的是从request queue中摘取request并最终交由SATA硬盘处理的过程,因此其他无关的内容不会过多涉及。

2. 转换流程

  虽然下面的内容仅针对AHCI,但是所讲述的request到ATA cmd的转换过程,对PATA或者AHCI的IDE mode也基本相同。

2.1 request queue是什么?从何而来?

  任何一个PCI Device,其驱动程序一定要实例化struct pci_driver结构,通常.probe成员被认为是初始化函数,也就是加载driver时第一个要被执行的函数(module_init()声明的函数才是第一个被执行的函数)。在AHCI driver的ahci_init_one(即.probe成员)函数中调用ata_host_register()来注册Controller,其中会调用scsi_alloc_sdev()来创建scsi device,在该函数中,创建了该scsi device对应的request queue,并指定了超时处理函数等信息。

  从scsi_alloc_queue()得知,从queue中摘取request的工作交由scsi_request_fn来完成,而队列中的request则是在提交了bio之后,由make_request_fn()负责转换并加入队列的。

2.2 request转换成SCSI cmd的过程

  第一步,尝试从queue中摘取request(因为scsi_request_fn的调用时机并不一定是有request插入到队列中,还有可能是完成一个request时,所以这里用“尝试”一词),如果摘取到request,并且可以向device发送cmd,则继续执行,否则跳出scsi_request_fn,等待下一次被调用。

  在这里,blk_peek_request()会从request queue中取出request,然后创建对应的SCSI cmd结构,并且调用prep_rq_fn()进行必要的初始化(例如分配sg list),special成员,后面会用到;scsi_dev_queue_ready可能返回0的条件是:device被block了,或者device已经满负荷,不能再接收新的cmd了。

  第二步,从request的special成员取出scsi_cmd结构体,也就是说special成员的实际结构就是scsi_cmd,并进行一些必要的赋值操作。

  第三步,调用scsi_dispatch_cmd(),进一步初始化scsi_cmd结构体(例如指定scsi_cmd的scsi_done回调函数等),并向下层转发SCSI cmd。

2.3 由SCSI cmd转换成ATA cmd

  接着2.2的第三步, scsi_dispatch_cmd()一路调用下来,会调用到__ata_scsi_queuecmd(),该函数的作用就是获取SCSI cmd到ATA cmd的转换接口函数,并且调用该接口正式将SCSI cmd转换成ATA cmd。具体地:

  首先,根据scsi cmd的操作码,查询是否有对应的转成ATA cmd的函数。这是针对硬盘的处理:

  下面是操作码对应的转换函数:

  从ata_get_xlat_func()可以看出,并不是每个SCSI cmd都能转换成ATA cmd(SCSI SPEC与ATA SPEC本就有两套不同的命令集),因此在实际运行过程中,有可能遇到该函数返回NULL的情形。对于能够找到转换函数的SCSI cmd,就调用相应的转换函数把它转换成ATA cmd,对于无法找到转换函数的SCSI cmd,就根据该cmd的功能,用其他手段来模拟该cmd的执行,只要达到执行该SCSI cmd的目的即可。

  例如READ_6(SCSI cmd)对应的转换函数是ata_scsi_rw_xlat(),该函数内部通过调用ata_build_rw_tf()来把SCSI cmd转换成task_file结构,该结构就代表了一个ATA cmd,早期的PATA driver中,可以直接把该结构的成员写入到Controller的各个寄存器中,从而触发Controller向PATA device发送ATA cmd。

2.4 ATA cmd被转换成FIS结构,然后发往硬盘

  AHCI Controller不能识别task file结构,它只能识别FIS结构,因此要想控制硬盘,还需要把ATA cmd转换成FIS结构,再填入AHCI Controller的寄存器中。这一切都是在Low Level Driver中完成的,这些工作是通过ata_scsi_translate()接口来封装。该函数分四步执行:

第一步:获取一个空闲cmd slot的tag

  tag用以表示“有权力”向Controller的寄存器中写入代表ATA cmd的FIS结构(后面提到的FIS指的就是ATA cmd对应的FIS结构),AHCI Controller共有32个cmd slot,用tag 0来表示第1个cmd slot,tag 31表示最后一个cmd slot,成功获取一个tag,就意味着获得对应的cmd slot,也就拥有了它所对应的内存资源、寄存器等的操作控制权。

  另外,还应当了解在32个cmd slot中,最后一个有特殊用途,因此,理论上Driver可以同时向AHCI Controller写入31个FIS结构,即AHCI Controller可以在无需driver参与的情况下连续向硬盘发送31个cmd。

第二步:把SCSI cmd对应的sg list赋给tag对应的qc结构的。

  qc结构体代表和维护一个ATA cmd,它包含的内容更加丰富(例如包含有tag信息、sg list信息等),Low Level Driver在操作ATA cmd相关的内容时就以qc为原料。

  对于有DMA传输的ATA cmd,那么一定需要sg list结构,ata_sg_init()仅仅是把SCSI cmd中的sg list信息赋给qc结构,实际上sg list是在2.2的blk_peek_request()中创建的。

第三步:指定qc的complete回调函数,即cmd完成后,会调用该函数。

  在中断处理函数中,完成一个ATA cmd之后,会从ata_qc_complete()开始一步步调用至该回调函数。

第四步:把ATA cmd转换成FIS,写入到AHCI Controller的寄存器中

  ata_qc_issue()的主体就是两个函数:一是qc_prep(),二是qc_issue(),它们在ahci driver中分别对应ahci_qc_prep(),和ahci_qc_issue()。

ahci_qc_prep()的工作:

  调用ata_tf_to_fis()把task file(即ATA cmd)转换成FIS,调用ahci_fill_sg()把sg list填写到寄存器中(即PRD table),调用ahci_fill_cmd_slot()填写cmd slot寄存器(32 Byte长的cmd header结构)。

ahci_qc_issue()的工作:

   触发PxCI寄存器(如果是NCQ cmd,还会同时写PxSACT寄存器),通知Controller向硬盘发送FIS结构吧。

request到ATA cmd的转换过程相关推荐

  1. 如何通过IOCTL_ATA_PASS_THROUGH发送ATA命令并获取ASTS(ATA task file status)

    如何发送ATA cmd?如何发送ATA cmd后如何获取ASTS状态? 发送了fd命令后想获取这里固件返回的ASTS里面的数据,该怎么做呢? 直接上代码: HANDLE GetIoCtrlHandle ...

  2. linux硬盘识别过程

    目录 1. 硬盘启动协议 2.SCSI总线扫描的方法 方法: 3. 内核打印信息 硬盘开机.内核函数跟踪打印信息 信息解读 硬盘热插拔.内核函数跟踪打印信息 信息解读 4. 硬盘识别过程 5. 硬盘识 ...

  3. Linux AHCI驱动

    目录 概念 port link device tag scsi host scsi device sgl Port.Link.Device之间的关系 Port Link host link pmp l ...

  4. C#读取数据库图片显示、缩小、更新

    ashx源文件代码: <%@ WebHandler Language="C#" Class="GetThumbnailImageByEmpCode" %& ...

  5. 使用salt-api来搭建salt自动化平台

    一.介绍 通常使用saltstack都是在master的服务器上直接命令操作,这个对于运维人员来说不是什么大事,但是也会有出错的时候,而一旦出错,就会有不可挽回的后果. 二.框架 这里使用django ...

  6. 应急响应-Yara规则木马检测

    Yara规则介绍 Yara是一个基于规则的恶意样本分析工具,可以帮助安全研究人员和蓝队分析恶意软件,并且可以在应急取证过程中自定义检测规则来检测恶意软件,Yara支持有木马文件落盘和无木马文件落盘(内 ...

  7. 使用VScode开发C语言程序,环境安装配置,保姆级教程

    墙裂推荐:C语言学习资源汇总,史上最全面总结,没有之一 首先我们要现在vscode和mingw64安装包 由于网络原因,vscode和mingw64的安装包下载比较慢,冲哥这里已经下载好了并上传到了网 ...

  8. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析

    PowerPC + Linux2.6.25平台下的SPI驱动架构分析 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人b ...

  9. iBatis.Net实现返回DataTable和DataSet对象

    如题.要返回一个ADO.NET对象好像没有使用ORM的必要,而且从编程的角度看这样的实现一点也不OO,但是实际的开发场景中还是会碰到这种需求的.下面我就借鉴前人的经验,结合实际的示例,再总结一下.如果 ...

最新文章

  1. nodejs yarn包管理工具
  2. python解决实际问题的代码_Python代码规范问题及解决
  3. 靠 GitHub 打赏谋生的程序员,他们是怎么做的?
  4. SAP CRM product customizing下载的第四个队列
  5. 从零开始系统化学Android,值得收藏!
  6. volatile用法
  7. assoc_Ruby assoc()函数
  8. python for循环文件每一行_python – 在使用for循环读取文件时跳过一行
  9. zabbix3.4监控java_CentOS7,zabbix3.4通过,zabbix-Java-gateway监控Tomcat
  10. 基于ZYNQ FPGA实现图像采集存储显示
  11. 指定某个git的版本代码拉取新的分支
  12. libjvm ThreadState Transition transition_from_native
  13. java实现归并排序
  14. 工具推荐:JDownloader - 下载网盘资料的好工具
  15. 基于树莓派的智能门禁及3D外壳打印设计
  16. win10重装系统后Mysql环境和数据的恢复(无需重装Mysql)
  17. 33暴力破解(MD5撞击)
  18. 计算机网络——路由器接口及静态路由配置
  19. 015A VLAN间路由
  20. IT人必须掌握的面试技巧

热门文章

  1. 为什么越来越多的大卖选择独立站?
  2. tensorflow.compat.v1 has no attribute contrib
  3. 小技巧:使用谷歌地图查询公交和地铁信息
  4. 留学生快速斩获IT名企实习offer的5大攻略
  5. 苹果Vision Pro物料成本曝光,3499美金售价原来是这样来的
  6. 免费OA与付费OA的区别体现在哪方面?该如何选择?
  7. 苹果上的Http Live Stream(HLS)技术初探
  8. java基础之类、对象、实例、属性、方法等概念理解
  9. jQuery Masonry构建pinterest网站布局注意要点(转)
  10. 计算机工作模式s0 s1,电源选项中S0,S1,S2,S3,S4,S5的含义.docx