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

2023-12-28 10:08

本文主要是介绍深入浅出SCSI子系统(六)SCSI 磁盘驱动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

深入浅出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 磁盘驱动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/545600

相关文章

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

Linux 安全弹出外接磁盘

命令行操作 首先,需要卸载硬盘上的所有分区,可以使用umount来卸载分区 清空系统缓存,将所有的数据写入磁盘 sync 列出已挂载的文件系统 使用lsblk或者df命令来查找要卸载的分区 lsblk or df -h 确保没有文件正在使用 使用lsof 命令来检查 sudo lsof |grep /dev/sdc 卸载分区 假设硬盘的分区是 /dev/sdc1,使用u

【虚拟化】AIO主机安装PVE8,配置网络,安装win11(virtio,qcow2,scsi,oobe,adk)

【虚拟化】AIO主机安装PVE8,配置网络,安装win11(virtio,qcow2,scsi,oobe,adk) 文章目录 1、ESXI vs PVE,AIO主机系统二选一2、PVE网络配置(DNS,换源,网卡,https,概览)3、win11虚拟化配置(virtio,raw,qcow2)附,域名解析,rocky9.4,黑群晖 1、ESXI vs PVE,AIO主机系统二选

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

Android/Linux 磁盘写入缓存/等待时间 参数修改

Linux系统当进行文件写操作时,并不会将数据立马写入磁盘,而是写写到缓存,等待达到占用内存一定比例或超过一定时间才会批量将这些缓存数据写入磁盘,这样可以减少IO操作,提升性能和磁盘寿命。如果数据还没来得及写入磁盘发生硬件掉电,这些数据就会丢失。应用可以调用sync实时将内容写入磁盘避免丢失。排查丢失问题可以在断电前执行sync命令,看能不能复现,若无法复现,说明就是缓存没有及时写入磁盘导致。

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

PageOfficeCtrl支持直接打开服务器磁盘文件

一般来说,PageOfficeCtrl控件的WebOpen方法的第一个参数是待打开文档的URL,此URL可以是相对于当前页面的相对URL,也可以是相对于整个网站根的相对URL,还可以是http开头的完整URL,但是这个URL必须是当前网站的URL,不能跨域。 现在为了更加方便开发者编程,WebOpen支持打开服务器磁盘文件。也就是说,第一个参数可以写成服务器文件的绝对磁盘路径。例如: P