scsi驱动开发基础

来源:互联网 发布:银行招聘程序员工资 编辑:程序博客网 时间:2024/06/05 06:17
scsi开发基础 
                
scsi驱动在内核中的位置
  scsi驱动的核心为总线层驱动,在总线层驱动之上为各种不同的scsi设备驱动,在总线层驱动之下为scsi host驱动。其在内核中的位置如下图所示:
  

  2.3 Linux中scsi驱动框架
  在Linux中scsi驱动基本分为三大层:top level,middle level以及lower level。top
level为具体的scsi设备驱动,例如我们常用的磁盘设备驱动就在该层(Linux中的实现为sd.c),scsi
disk的驱动向上表现为块设备,因此,具有块设备的接口及一切属性,向下表现scsi设备,因为scsi
disk基于scsi总线进行数据通信。top
level驱动与具体的scsi设备相关,所以该类驱动往往由设备开发者提供,但是如果scsi设备为标准类设备,那么驱动可以通用。middle
level实际上就是scsi总线层驱动,按照scsi协议进行设备枚举、数据传输、出错处理。middle level层的驱动与scsi
specification相关,在一类操作系统平台上只需实现一次,所以该类驱动往往由操作系统开发者提供。lower
level为scsi控制器的驱动,该驱动与具体的硬件适配器相关,其需要与scsi middle
level层进行接口,所以往往由提供适配器的硬件厂商完成驱动开发,只有硬件厂商才对自己定义的register
file(寄存器堆)最清楚。当然,在lower level层可以做虚拟的scsi host,所以该层的驱动也不一定对硬件进行操作。
  Linux中,scsi三层驱动模型如下图所示:

  三层驱动模型进行了完善的功能划分,并且定义了很好的接口,便于不同开发者提供的产品能够无缝集成。本文将重点讨论操作系统提供的scsi middle level驱动的实现机制及功能接口。
  3、scsi middle level层重要数据结构
  3.1 scsi device的抽象

  在scsi middle level定义了scsi device的数据结构,用于描述一个scsi的具体功能单元,其在scsi host中通过channel、id、lun进行寻址。
  在scsi
host中可以存在多个channel,每个channel是一条完整的scsi总线,在scsi总线上可以连接多个scsi节点,每个节点采用id进行
编号,编号的大小与具体的scsi
specification相关,与总线层的驱动能力等因素相关。每个节点可以根据功能划分成多个lun,每个lun才是我们通常所说的scsi设备。这
种逻辑可以采用如下的总线拓扑结构描述:

  通过上述描述可以知道scsi_device是对lun的抽象。下面对scsi_device中的重要域进行说明:

  在scsi总线probe的过程中,scsi middle level会为每个lun抽象成scsi device,实现的核心函数为scsi_probe_and_add_lun()。
  3.2 scsi host的抽象
  scsi host的语义很清晰,其描述了一个scsi总线控制器。在很多实际的系统中,scsi
host为一块基于PCI总线的HBA或者为一个SCSI控制器芯片。每个scsi
host可以存在多个channel,一个channel实际扩展了一条SCSI总线。每个channel可以连接多个scsi节点,具体连接的数量与
scsi总线带载能力有关。scsi host的重要域描述如下:

  3.3 scsi target的抽象
  scsi target对scsi总线上的scsi node进行了抽象。每个scsi target可能拥有多个lun,即多个scsi devie。scsi target数据结构中的重要域定义如下: 

  3.4 low-level接口方法——
scsi
_host_template

  scsi middle level通过scsi_host_template接口调用scsi host的具体方法。在scsi host
driver向middle level注册host对象的同时需要注册scsi_host_template方法,该方法被注册到scsi
host对象中。
  scsi_host_template数据结构中的重要域说明如下:


点击看原图
  一个典型的scsi_host_template方法定义如下:

  4、关键函数分析
  4.1 scsi_scan_host函数

  scsi middle level层提供了scsi host扫描函数,在设备枚举过程中scsi host可以调用该函数对scsi总线适配器进行扫描,当然host
驱动
也可以调用更加底层的函数对scsi总线进行扫描。scsi_scsn_host函数实现流程如下:

  4.2 scsi_request_fn函数
  scsi_request_fn函数为scsi设备请求队列处理函数,该函数通常被注册到
request_queue->request_fn上。块设备请求的bio最终会merge到request
queue中,然后通过unplug_fn函数调用request_queue->request_fn,实现scsi_reuqest_fn函数
的调用。
  Scsi_request_fn函数实现了请求队列的处理,首先从请求队列中摘取一个request,然后通过
q->prep_rq_fn函数将请求转换成scsi命令,并且对scsi
command进行初始化,最后通过scsi_dispatch_cmd函数将scsi命令分发给底层的scsi host驱动。
  在scsi_request_fn函数的实现过程中,需要通过块设备发下来的请求构造相应的scsi命令,而scsi命令的生成与具体的设备驱
动相关,其需要调用设备驱动提供的scsi命令初始化函数*_init_command完成命令初始化过程。假设请求发送给scsi
disk设备,那么在各层之间的函数调用关系如下图所示:

  从前面分析可以看出,请求队列queue是top level与middle
level之间的纽带。上层请求会在请求队列中维护,处理函数的方法由上下各层提供。在请求队列的处理过程中,将普通的块设备请求转换成标准的scsi命
令,然后再通过middle level与low level之间的接口将请求递交给scsi host。
  4.3 scsi_dispatch_cmd函数
  scsi_dispatch_cmd函数将一个scsi命令提交给底层scsi
host驱动。在命令dispatch的过程中,middle level会检查scsi
host是否出于busy状态,是否还有空间存放新的scsi
command。如果所有条件都满足,那么会调用上下层之间的接口函数queuecommand函数转发请求。
  Queuecomand函数的实现由scsi host
driver完成。通常该函数的实现很简单,只需要将传下来的scsi命令挂载到host的scsi命令队列中。由于queuecommand函数在持有
spinlock的上下文中运行,所以不宜做过多复杂的操作,否则很容易导致程序睡眠,从而使程序运行不稳定。
  5、scsi设备扫描过程描述
  在计算机系统启动过程中,操作系统会扫描默认的PCI根节点,从而触发了PCI设备扫描的过程,开始构建PCI设备树。
  scsi host作为PCI设备会被PCI总线驱动层扫描到(PCI设备的扫描采用配置信息读取的方式),扫描到scsi
host之后,操作系统开始加载scsi host的驱动,scsi host driver就是上面说所的low level
driver。scsi host
driver初始化scsi控制器,通过PCI配置空间的信息分配硬件资源,注册中断服务。最后开始扫描通过scsi控制器扩展出来的下一级总线——
scsi bus。
  scsi bus的扫描通过scsi middle level提供的服务完成。scsi host driver可以调用scsi middle level提供的扫描算法完成scsi总线设备的扫描,扫描过程可以描述如下:


点击看原图
  在scsi总线扫描过程中用到了scsi middle level层的如下重要函数:
  1、scsi_scan_host:对scsi host设备进行扫描。
   2 
scsi
_add_device:探测具体的device,并且将其加入系统。 
  3、scsi_probe_and_add_lun:探测具体指定的lun,并且将其加入系统。
  4、scsi_probe_lun:采用INQUIRY命令对lun节点进行探测。
  5、scsi_add_lun:加入lun节点并且初始化SCSI设备。
  scsi总线scan过程中的函数调用情况如下图所示:

  6、 scsi设备读写过程
  在此给出一个scsi设备的读写数据流程,通过该例子,读者可以方便查找
Linux

代码,并且能够理清繁杂的代码结构。假设读写的scsi设备为scsi
disk设备,数据首先通过文件系统,进入到文件系统的Cache。文件系统的pdflush
daemon会将Cache住的数据刷新到磁盘,其根据buffer
head的内容构造bio,然后调用块设备接口(submit_bio)将请求发送给块设备层。bio在块设备层多次转发,最后被merge到块设备的请
求队列中。请求可能会在请求队列滞留一段时间,然后在软中断或者用户上下文中调用request_fn去处理请求队列。在scsi middle
level
驱动
层,
块设备的请求被转换成scsi command,然后通过queuecommand函数接口将scsi command提交给scsi
host,通常scsi
host会发起DMA操作将数据传输给具体的设备。至此,数据从应用程序转移到了scsi设备,当然上述过程还没有涉及到回调过程,实际的回调会在中断上
下文、软中断上下文中完成,在请求发送的每一层都保存了相应的回调上下文。整个数据流的过程中,涉及到的函数如下:

  7、scsi middle level层驱动设计思想总结
  scsi middle level还有很多实现细节没有探讨,很多细节性的问题会在后继的blog中给出,这里只是给出了scsi
middle level的框架性东西,意在对scsi总线层有个提纲挈领的效果。在分析scsi middle level的过程中,有如下几点感想:
  1、  scsi驱动采用了规范的分层设计思想,其一共分为三层,分层之后使得设计分工更加明确,而且在逻辑上也更加清晰,设计工作也更加简单。
  2、 
scsi驱动中比较固定的层次为scsi middle
level,该层可以称之为scsi通用中间层,或者为总线驱动层。该层向上和向下都需要提供接口,所以上层驱动开发时需要注册相关接口函数,下层驱动工
作时也需要注册接口函数,只有这样中间层才可以很灵活的进行上下层数据传输。
  scsi middle level主要实现了scsi总线扫描算法,scsi命令转换算法,scsi出错处理等机制,这些东西都是scsi的核心所在。
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 快递没收到点了确认收货怎么办 快递没收到自动确认收货怎么办 房地产股市汇率一齐暴跌怎么办 尼日利亚落地签过期了怎么办 期货亏光了所有怎么办 期货钱亏完了该怎么办 做黄金亏损500万怎么办 炒黄金被骗35万怎么办 淘宝发货填错单号怎么办 发货单号填错了怎么办 发快递忘了单号怎么办 国际物流查不到物流怎么办 纸币上印邪教该怎么办 钥匙掉到电梯缝里怎么办 汽车电子钥匙铜线折一根怎么办 防盗门的锁不好开怎么办 同学帮刷饭卡说不用还钱了怎么办 em231电源指示灯不亮怎么办 运行广联达卡住怎么办 马桶被粪便(大便)堵了怎么办 子宫壁厚12mm怎么办 管子太多每次洗澡都是冷水怎么办 热水冷水装反了怎么办 大树被高锰酸钾灌溉了怎么办会死吗 防盗门门被锁了怎么办 门被里面反锁了怎么办 门里面被锁了怎么办 被锁在门里怎么办 门锁住了没钥匙怎么办 车被别人锁住了怎么办 汽车轱辘被锁了怎么办 小车轮胎被锁了怎么办 国防光缆无明显标识被挖断怎么办 临工210挖掘机柴油进气怎么办 汽车抛光蜡干了怎么办 洗碗铁丝球吃了怎么办 牙套铁丝吃肚子怎么办 小铁丝吃到肚子怎么办 绿色抛光膏干了怎么办 不锈钢被盐酸弄黑了怎么办 不锈钢被草酸洗了怎么办