Linux中SCSI总线的磁盘扫描与盘符管理

本文发布时间: 2019-Mar-22
1、写在前面Scsi总线在扫描磁盘设备后生成的盘符与设备通道之间的关系是不固定的,其最主要的原因是设计者考虑到scsi总线在系统中不会静态、唯一存在,会动态生成,而盘符空间在全局只有一个,因此,盘符与设备通道之间很难实现绑定,至少这种绑定关系会随着系统中scsi总线的增加而遭到破坏。所以,设计者采用了动态映射的方法维护盘符与设备通道之间的关系。盘符与设备通道之间的动态映射会影响到存储设备的管理。例如,一个存储设备由于某种原因拆除了一个磁盘,重新启动之后,所有的盘符将会重新生成,从而会导致磁盘上层的存储软件无法正常启动,除非这些存储软件能够自动识别磁盘设备,然后进行重构。显而易见,盘符与设备通道之间的动态映射增加了存储设备管理的难度。为了降低存储设备的管理难度,需要固定盘符与磁盘通道之间的映射关系。考虑到存储设备的硬件资源相对固定,所以,这种映射关系在理论上是可以固定的。为此,本文从LinuxSCSI层磁盘扫描的角度对这个问题进行分析、总结。2、磁盘扫描算法描述通常SCSI总线适配器作为PCI设备的形式存在,其在计算机体系结构中的位置描述如下图所示:图1scishost及device在计算机体系结构中的位置在系统初始化时会扫描系统PCI总线,由于scsihostadapter挂接在pci总线上,因此会被pci扫描软件扫描得到,并且生成一个pcidevice(PDO)。然后扫描软件需要为该pcidevice加载相应的驱动程序。在linux系统中,扫描软件会遍历pcibus上存在的所有驱动程序,检查是否有符合要求的驱动程序存在。这里假设scsihost是marwell的设备,那么,如果存在marwell提供的scsihostdriver,就会被成功调用。加载scsihost驱动时,pci扫描程序会调用scsihostdriver提供的probe函数,该probe函数是scsihostdriver在初始化驱动时注册到pci-driver上的(Linux的总线驱动都是采用的这种思路)。在scsihost具体的probe函数中会初始化scsihost,注册中断处理函数,并且调用scsi_host_alloc函数生成一个scsihost,然后添加到scsimiddlelevel,最后调用scsi_scan_host函数扫描scsihostadapter所管理的所有scsi总线。一个scsihostadapter可能拥有多个channel,每个channel拥有一条scsi总线。传统scsi总线是并行共享总线,现有的SATA、SAS等P2P接口在逻辑上可以理解成总线的一种特例,所以scsimiddlelevel驱动程序是通用的。由于一个scsihost可能存在多个channel,因此依次扫描每个channel。按照spec,传统scsibus上最多可以连接16个scsitarget,因此,scsi扫描程序会依次探测target。一个scsitarget可以存在多种功能,每种功能称之为LUN,对于单功能设备(例如磁盘),其LUN通常为0。Scsihost的扫描过程可以简单采用如下伪码进行描述:For (channel = 0; channel < max_channel; channel++) {/* 对一个适配器的每个通道中的设备进行识别 */…For (id=0; id<max_id; id++) {/* 对一个通道中的每个ID对应设备进行识别 */...For (lun=1; lun<max_dev_lun; lun++) {/* 对一个ID对应设备的每个LUN进行识别 */...}}}通过上述扫描过程可以知道,在系统中可以采用如下方法对一个scsidevice进行描述:host_id:channel_id:target_id:lun_id其中,host_id是系统动态分配的,这与PCI总线的扫描顺序相关,对于固定硬件的系统host_id扫描得到的结果不会改变,但是,如果动态添加一个scsihost(PCIdevice),系统的host_id可能会发生变化,这一点需要注意。3、盘符名称生成方法3.1盘符名称生成概述在SCSIDiskprobe的过程中,内核会为scsidisk分配盘符名称。由于scsidisk的盘符空间是全局的,而系统中的scsihost、scsitarget以及scsilun都会动态变化,所以,Linux系统没有办法为每个scsidisk分配一个固定的、简单易用的盘符名称。因此,Linux对scsidisk的盘符采用动态分配的策略。Linux在内核中维护了一棵管理设备盘符名称的树(idr_tree),当系统每次probe到一个新设备之后,都会从idr_tree中查找一个空闲节点,并且获取该空闲节点的id号,作为盘符的索引名称。例如,当系统扫描到一个新的scsidisk之后,会从idr_tree中查找并且获取一个空闲节点的id号,然后系统再将id号映射成26个英文字母,最后与”sd”一起组合成一个新的字符串,该字符串就是scsidisk的盘符。该机制的优势在于盘符管理简单,缺点在于对于一个动态系统,盘符的名称可能发生变化,影响到上层应用系统。3.2Idr_tree算法描述Idr_tree是内核提供的一种算法,其实现了整型id号与一个内核指针之间的映射。Idr_tree算法的实现采用了radix_tree的思想,其数据结构定义与radixtree有着类似之处。Idr_tree在2003年的时候引入至内核,目前在scsi盘符空间的管理、i2c总线设备号与设备结构之间的映射方面都有应用。在整型id与内存指针之间的映射可以采用数组的方式,但是随着存储规模的增大,数组的方式显然不能满足应用的需求;另一种方式可以采用链表进行映射信息的管理,同样不能管理较大的映射资源。所以,在Linux中采用树进行映射资源的管理。这种树就是idr_tree。采用idr_tree来管理整型id与内存指针之间的映射关系具有如下两方面对优势:1、具有很好的映射关系查找效率。2、能够很好的扩展存储规模,动态增大树的规模对查找性能影响不是很大。Idr_tree的结构示意图描述如下图所示:图idr_tree结构示意图Idr_tree的叶子节点用于存储用户定义的数据,通常用于存储用户需要查找的内存指针。树中的每个节点定义为idr_layer,该数据结构中包含了一个bitmap位图,用于描述下层节点的属性。当下层节点不存在空穴(空闲节点)时,上层节点bitmap位图中的对应位置位。因此在查找过程中,从顶层节点开始,判断每个节点的bitmap信息,然后遍历第一个bit位为0的下层节点,直到遍历到叶子节点为止。由此可见,Idr_tree的算法复杂度与树的层次数量相关。Idr_tree是动态生成的,随着叶子节点的增多,树的层次将会不断增大。


(以上内容不代表本站观点。)
---------------------------------
本网站以及域名有仲裁协议。
本網站以及域名有仲裁協議。

2024-Mar-04 02:10pm
栏目列表