目录

深入浅出SCSI子系统(二)SCSI 磁盘驱动

1.SCSI磁盘驱动重要结构体:

3.异步执行部分

3 重新校验磁盘

4 让磁盘转起来


深入浅出SCSI子系统(二)SCSI 磁盘驱动

Linux SCSI子系统实现了各种类型的高层驱动,如SCSI磁盘驱动、SCSI磁带驱动和SCSI CDROM驱动等。SCSI磁盘驱动对应的文件sd.c

SCSI总线类型scsi_bus_type   SCSI设备和SCSI驱动 -->sd_probe()

在SCSI设备被发现,或者SCSI(高层)驱动被加载时,会在SCSI总线类型上和驱动链表中驱动,或者设备链表中的设备尝试绑定。在SCSI总线类型(scsi_bus_type,文件drivers/scsi/scsi_sysfs.c)中定义的match回调函数将首先被调用。

1.SCSI磁盘驱动重要结构体:

(1)SCSI磁盘结构体scsi_disk

这个结构中看到了一个指向通用磁盘描述符的指针,即disk域,看到了指向SCSI设备描述符的指针,即device域,还有一个指向SCSI驱动描述符的指针,即drives域。

在Linux中,用三个结构来共同表示一个SCSI磁盘,每个结构反映了SCSI磁盘的某方面属性:

• scsi_device:给出了作为SCSI设备方面的属性;

• scsi_disk:给出了作为SCSI磁盘方面的属性;

• gendisk:给出了作为通用磁盘方面的属性。

(2)SCSI驱动结构体 scsi_driver

(3)SCSI磁盘驱动实例为sd_template,定义在文件drivers/scsi/sd.c

PCI子系统和SCSI子系统的工作模式:

(1)PCI子系统的工作模式:PCI核心注册PCI总线类型,扫描PCI总线得到所有的PCI设备。PCI HBA驱动注册PCI设备驱动,逐个和PCI设备根据设备ID匹配;

(2)SCSI子系统的工作模式:SCSI中间层注册SCSI总线类型,SCSI较高层注册SCSI设备驱动,SCSI较低层(SCSI HBA驱动)则负责扫描SCSI总线得到所有的SCSI设备。SCSI设备将和SCSI设备驱动的匹配是根据SCSI INQUIRY响应中的SCSI设备类型域。

2. 同步执行部分

sd_probe函数是SCSI磁盘驱动的实际入口。它在驱动初始化期间,以及有新的SCSI设备被挂载到系统中时被调用。对于每个出现的SCSI设备(不仅仅是磁盘)都会调用一次。这个函数被从SCSI中间层调用,它在给定的<host, channel, id, lun>和新的设备名(如/dev/sda)之间建立映射。更精确地说,在这里选定了块设备的主设备编号和次设备编号。

static int sd_probe(struct device *dev):这个函数具有唯一的参数,即驱动模型中的设备描述符指针。这个指针实际上指向SCSI设备的内嵌通用设备,即sdev_gendev域,因为SCSI设备是通过内嵌通用设备链入SCSI总线类型(scsi_bus_type)的设备链表的。若成功,这个函数返回0;否则返回错误码。

static int sd_probe(struct device *dev)
{
1.struct scsi_device *sdp = to_scsi_device(dev);//获取scsi磁盘设备描述符2.struct scsi_disk *sdkp;sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);//分配scsi_disk磁盘描述符3.struct gendisk *gd;gd = alloc_disk(SD_MINORS);//分配gendisk。gendisk描述符的分配相对复杂,需要处理分区,因此调用专门的alloc_disk函数。对于SCSI磁盘,系统固定可以有15个分区,加上作为整体使用的磁盘,一共是SD_MINORS(16)个,作为参数传入。4.error = ida_get_new(&sd_index_ida, &index);error = sd_format_disk_name(“sd”, index, gd->disk_name, DISK_NAME_LEN);//为SCSI磁盘分配设备名,设备名为sda-sdz,sdaa-sdzz, sdaaa-sdzzz5.sdkp->device = sdp;sdkp->driver = &sd_template;sdkp->disk = gd;//构建SCSI磁盘各种结构关系中的一部分,将scsi_disk的device、driver及disk域分别指向scsi_disk、scsi_driver及gendisk描述符。6.device_initialize(&sdkp->dev);//初始化scsi磁盘内嵌devicesdkp->dev.parent = dev;sdkp->dev.class = &sd_disk_class;dev_set_name(&sdkp->dev, "%s", dev_name(dev));device_add(&sdkp->dev)//为scsi磁盘在sysfs中创建目录
/*SCSI磁盘在sysfs文件系统中创建目录。由于SCSI磁盘的内嵌设备有parent(为SCSI设备的内嵌通用对象),有class(为sd_disk_class),且parent没有class,该目录会在SCSI设备目录中创建在以类名(scsk_disk)为名字的子目录下,目录名字被设置成SCSI设备名。举例说明,/sys/devices/pci0000:00/0000:00:10.0/host2/target2:0:0/2:0:0:0/scsi_disk/2:0:0:0。*/}

下图:SCSI设备及其派生的SCSI磁盘之间的驱动模型设备关系

3.异步执行部分

sd_probe_async函数解析:

static void sd_probe_async(void *data)
{
1.struct scsi_device *sdp;struct scsi_disk *sdkp = data;struct gendisk *gd;sdp = sdkp->device;gd = sdkp->disk;index = sdkp->index;dev = &sdp->sdev_gendev;//驱动模型中的设备描述符
//通过已存在的关系,找到指向SCSI设备描述符的指针以及指向通用磁盘描述符的指针。2.    gd->fops = &sd_fops;//块设备操作表指针指向sd_fopsgd->private_data = &sdkp->driver;//private_data域指向scsi_disk结构中driver域的地址gd->queue = sdkp->device->request_queue;//queue指向SCSI设备描述符的请求队列3.sd_revalidate_disk(gd); //该函数的第一次调用,在add_disk之前进行
(1)sd_spinup_disk(sdkp);//向scsi磁盘发送命令,让磁盘转动起来
(2)sd_read_capacity(sdkp, buffer); //读取磁盘容量
(3)sd_read_block_limits(sdkp);sd_read_block_characteristics(sdkp);
(4)sd_read_write_protect_flag(sdkp, buffer);//读取磁盘写保护标志
(5)sd_read_cache_type(sdkp, buffer);//读取磁盘缓存类型
(6)sd_read_app_tag_own(sdkp, buffer)//读取磁盘ATO位
(7)sdkp->first_scan = 0;//此标志是判断是否函数第一次调用
(8)blk_queue_ordered(sdkp->disk->queue, ordered, sd_prepare_flush);//根据SCSI磁盘特性确定IO的屏障能力
(9)set_capacity(disk, sdkp->capacity);//根据INQUERY命令查询到的磁盘容量设置4.blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);//将scsi请求队列的prep_rq_fn设置为sd_prep_fn此后不仅可以处理来自scsi层的请求,也可以处理来自通用io层的请求,可以为bio请求构建scsi命令5.gd->driverfs_dev = &sdp->sdev_gendev;//sysfs文件系统中联系通用磁盘和scsi设备,每个SCSI device对应一个通用磁盘6.dev_set_drvdata(dev, sdkp);//将scsi磁盘描述符指针赋值给sdp->sdev_gendev->drv_data域,sdp为scsi_device类型指针scsi磁盘驱动的其它回调函数(如sd_rescan, sd_shutdown等)都需要拿到scsi磁盘描述符7.add_disk(gd);//将通用磁盘添加到系统8.SCSI磁盘探测已经完成,我们在输出消息后返回。}

3 重新校验磁盘

在SCSI磁盘探测过程中,两次调用sd_revalidate_disk。这是有意安排的,让在add_disk之前和之后两次调用sd_revalidate_disk。其原因在于向块I/O子系统确定注册的方式。

sd_revalidate_disk()
{
1.sd_spinup_disk()//向SCSI磁盘发送命令让磁盘转动起来2.sd_read_capacity(sdkp, buffer);//读取磁盘容量3.sd_read_write_protect_flag()//读取SCSI磁盘的写保护标志4.sd_read_cache_type()//读取SCSI磁盘的缓存类型5.sd_read_app_tag_own()//读取SCSI磁盘的ATO位,即确定操作系统释放可以访问它的DIF三元组中的应用程序标签
//上述函数都是通过向SCSI磁盘发送命令,并查询返回的数据格式。6.sdkp->first_scan = 0;//通过first_scan域判断sd_revalidate_disk函数及它调用到的上述函数到底是在第一次运行时(add_disk之前),还是在第二次运行时(add_disk之后)。7.blk_queue_ordered(sdkp->disk->queue, ordered, sd_prepare_flush);//根据SCSI磁盘特性确定IO的屏障能力  将冲刷磁盘缓存的回调函数设置为sd_prepare_flush:最终调用SCSI命令SYNCHRONIZE_CACHE来实现将磁盘缓冲写到介质上8.set_capacity(disk, sdkp->capacity);//根据INQUERY命令查询到的磁盘容量,设置通用磁盘结构中的capacity域设置}

4 让磁盘转起来

sd_spinup_disk函数尝试等到SCSI磁盘已经准备好,必要时使磁盘转起来。前者发送TEST UNIT READY命令,后者发送START UNIT命令。

两层循环,外层循环的目的为让SCSI磁盘“准备好”给予足够的等待时间。

内层循环发送TEST_UNIT_READY命令,如果已知磁盘介质不存在,情况就简单了,可以直接返回。否则给它三次机会以忽略UnitAttention。

内层循环退出,根据其结果,有三种情况可以直接退出外层循环:(1)TEST_UNIT_READY成功,这是显而易见的;其他情况都表示设备还没有准备好,需要在外层循环进一步处理。所以:(2)因为处理必须给予感测数据,但我们拿不到有效的感测数据(3)所谓处理,是发送START_STOP命令,等待磁盘转动起来,但SCSI设备却指定了no_start_on_add域,不希望发送自动启动命令。

外层循环的剩余代码都是处理设备未准备好的情况的,又有以下三个分支:

• 如果感测数据为NOT_READY,向SCSI磁盘发送START UNIT命令,在此之前需要排除三种情况,这些都是通过附加感测码判断的:(1)需要手工介入,(2)磁盘正待机,(3)磁盘不可用。

• 如果感测数据为UNIT_ATTENTION,并且附加感测码为0x28,说明可能是USB闪存设备的情况。它和上一种情况一样,都在spintime为0的情况下设置它,开始或继续外层循环。

• 其他的感测数据都是不支持的,退出外层循环。退出外层循环后,根据结果可能输出一些内核消息,本函数即返回到调用者。

深入浅出SCSI子系统(六)SCSI 磁盘驱动相关推荐

  1. Linux内核源码分析-scsi子系统-让磁盘转起来-sd_spinup_disk

    如何调用到sd_spinup_disk接口,请查看前面文章[深入浅出SCSI子系统(六)SCSI 磁盘驱动] 链接:深入浅出SCSI子系统(六)SCSI 磁盘驱动_sinat_37817094的博客- ...

  2. 深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构

    目录 深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构 1.Linux 内核中的 SCSI 架构描述 2.SCSI 较高层 3.SCSI 中间层 4.SCSI 较低层 深入浅出SCSI ...

  3. Linux内核SCSI子系统(1)基本介绍

    文章目录 概述 SCSI子系统分层架构 SCSI设备模型 SCSI设备命名 磁盘设备命名 CD-ROM设备命名 磁带设备命名 sg设备命名 相关参考 概述 在Linux内核中,SCSI子系统主要承担驱 ...

  4. Linux存储IO栈(4)-- SCSI子系统之概述

    概述 Linux SCSI子系统的分层架构: 低层:代表与SCSI的物理接口的实际驱动器,例如各个厂商为其特定的主机适配器(Host Bus Adapter, HBA)开发的驱动,低层驱动主要作用是发 ...

  5. SCSI子系统基础学习笔记 (之UFS子系统) - 1.2 概述(软件部分)

    目录 1. 前言 2. UFS子系统软件架构 3. UFS模块框图 4. UFS领域模型 参考文档 1. 前言 本专题我们开始学习SCSI子系统的相关内容.本专题主要参考了<存储技术原理分析&g ...

  6. SCSI子系统基础学习笔记 (之UFS子系统) - 2.1UFS子系统初始化之ufs_qcom_probe

    目录 1. 前言 2.ufs_qcom_probe |- -ufshcd_alloc_host |- -ufshcd_init 参考文档 1. 前言 本专题我们开始学习SCSI子系统的相关内容.本专题 ...

  7. SCSI子系统基础学习笔记 (之UFS子系统) - 2.2 UFS子系统初始化之ufshcd_async_scan

    目录 1. 前言 2. ufshcd_async_scan |- -ufshcd_probe_hba |- -ufshcd_add_lus 3. 总结 参考文档 1. 前言 本专题我们开始学习SCSI ...

  8. SCSI子系统基础学习笔记 (之UFS子系统) - 3. UFS命令处理

    目录 1. 前言 2. scsi_queue_rq |- -ufshcd_queuecommand 3. ufshcd_intr 1. 前言 本专题我们开始学习SCSI子系统的相关内容.本专题主要参考 ...

  9. Windows磁盘驱动基础教程

    本文讲述Windows磁盘驱动的主要结构功能与编写方法基础.本文描述的内容仅限于软件层面,并不与具体的硬件相关. 1.磁盘驱动基础 不少人把文件系统驱动和磁盘驱动混为一谈.实际上文件系统驱动应该与磁盘 ...

  10. [ZZ]Windows磁盘驱动基础教程

    本文讲述Windows磁盘驱动的主要结构功能与编写方法基础.本文描述的内容仅限于软件层面,并不与具体的硬件相关. 1.磁盘驱动基础 不少人把文件系统驱动和磁盘驱动混为一谈.实际上文件系统驱动应该与磁盘 ...

最新文章

  1. 全球及中国特殊教育行业十四五趋势展望与发展战略建议报告2022版
  2. is running beyond the ‘VIRTUAL‘ memory limit. Current usage: 123.5 MB of 1 GB physical memory used
  3. [virtualenvwrapper] 命令小结
  4. ActionScript 3.0入门:Hello World、文件读写、数据存储(SharedObject)、与JS互调
  5. python 方程组 整数解_用Python语言求解线性整数方程组
  6. Git笔记(28) 签署工作
  7. php静态类 使用场景,【php】PHP静态方法和非静态方法的使用场景
  8. 【项目经验】之线程基本知识
  9. 滴滴 App 强制调取用户通讯录;子弹短信仍能泄露信息;特朗普炮轰谷歌 | 极客头条...
  10. 演示:扩展ACL的配置与应用技巧
  11. 【测验8 编程题】: 程序设计方法学 (第8周)
  12. 基于QT实现的钢琴软件 (MFC大作业)
  13. 彻底解决win10黑屏,睡眠后无法唤醒(Intel平台)
  14. 20230106 作业
  15. 快看!如何实现快速赢利--国内期货反跟单
  16. Flutter 项目实战 登录界面 (一)
  17. 针对“扫雷“和“植物大战僵尸“游戏,分析,扫描,阳光值,植物,金币,僵尸的分析逆向
  18. 市场调研—全球及中国细胞模型行业研究及十四五规划分析报告
  19. Java语法基本概念
  20. 疑难杂症篇(十)--Catia软件出现“没有合适的许可证来实现xx的请求”解决方案

热门文章

  1. 如何解决VC 应用程序无法启动,因为应用程序的并行配置不正确 sxstrace.exe问题...
  2. 南海区妇幼保健院HIS数据容灾备份系统项目
  3. arcgis伪节点检查_ARCGIS 拓扑检查步骤与修正拓扑错误技巧
  4. pandas填充空数组_Pandas 数据处理:空值清理、联级、合并、缺省空值填充
  5. MyBatis 拦截器执行顺序
  6. linux系统新磁盘可视化挂载方法
  7. 航拍地形图转换成地形图_无人机航测生成地形图技术流程(Pix4D+ArcGIS+CASS)...
  8. Spark2.1.1中用各种模式运行计算圆周率的官方Demo
  9. 汽车行业DBC文件解析 | Python 解析dbc文件
  10. 推荐几个浏览器插件帮助你查论文显示期刊等级(分区及影响因子),sci文章便捷下载