sas控制器驱动结构粗探--基于3.10.0-693.25.4

xiaoxiao2025-11-08  6

部门测试环境最近出了个内核core,是宕机在了mpt3sas这个模块,以前没见过这个模块,怎么查这个core呢?以前没见过,现在见见就好了;)。这个模块是sas控制器的驱动,在之前的IO栈研究中,只了解到过通用块层,scsi往下的就没接触了,也正好趁此解这个bug的机会了解下IO栈scsi以下是什么。

从config描述可以看到,mpt3sas是SAS控制器驱动,特殊点在于它是基于Fusion-MPT 架构的。

config SCSI_MPT3SAS tristate "LSI MPT Fusion SAS 3.0 Device Driver" depends on PCI && SCSI select SCSI_SAS_ATTRS select RAID_ATTRS ---help--- This driver supports PCI-Express SAS 12Gb/s Host Adapters.

Fusion-MPT是什么呢?先看网络上的一段描述

Fusion-MPT 技术由 LSI Logic 开发,旨在为客户提供更为容易的实现 SCSI 和 Fibre Channel 的解决方案。 Fusion-MPT 技术主要包括 Fusion-MPT 固件,SAS、U320 SCSI、Fibre Channel硬核,和操作系统级的驱动程序等部分。 Fusion-MPT 架构中使用统一的固件及驱动来支持所有基于 Fusion-MPT 技术的 I/O 控制器。 Fusion-MPT 架构可分为操作系统层和硬件层两部分,而从驱动程序设计的角度,又可进一步将其分为驱动、固件和硬件三个功能层次。 Fusion-MPT 在硬件层之上构建独有的固件层,不同的固件为上层驱动程序提供对 SCSI 或 FC 的支持,以及高级的集成 RAID 等功能。固件层有效地将驱动程序同硬件隔离,对上层驱动程序提供统一的 MPI ( Message Passing Interface )接口,使同一驱动程序可以应用于不同的底层硬件系统,有助于加速应用开发。驱动层对上层操作系统提供功能函数接口,通过 MPI 访问固件层,实现操作系统对硬件的访问,并且按照通信协议实现相关的帧封装和拆解。消息传递接口 MPI 提供了一个消息传递传输架构,它定义了主机与 LSI Fusion-MPT 芯片组通信的接口。

上述描述的很明白了,概括来说,Fusion-MPT是个好东西,它在硬件上提供了一个固件,上层驱动程序可以用MPI接口访问固件,而固件可以接不同的硬件,这样简化了操作系统对不通硬件的访问。而mpt3sas这个驱动,是用了MPT固件,下面接的是sas接口,我们看下这个设备在系统中的位置,LSISAS 1068E就是用mpt3sas的一个设备 依据资料简单分析下mpt3sas驱动的结构,

static int __init _mpt3sas_init(void) { int error; #ifdef MPT2SAS_SCSI pr_info("%s version %s loaded\n", MPT2SAS_DRIVER_NAME, MPT2SAS_DRIVER_VERSION); #else pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME, MPT3SAS_DRIVER_VERSION); #endif /* MPT2SAS_SCSI */ mpt3sas_transport_template = //设置template sas_attach_transport(&mpt3sas_transport_functions); if (!mpt3sas_transport_template) return -ENODEV; #ifdef MPT2SAS_SCSI mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions); if (!mpt2sas_raid_template) { sas_release_transport(mpt3sas_transport_template); return -ENODEV; } #else mpt3sas_raid_template = raid_class_attach(&mpt3sas_raid_functions); if (!mpt3sas_raid_template) { //设置template sas_release_transport(mpt3sas_transport_template); return -ENODEV; } #endif /* MPT2SAS_SCSI */ error = scsih_init(); //注册一系列回调函数 if (error) { scsih_exit(); return error; } #ifdef MPT2SAS_SCSI mpt3sas_ctl_init(1); #else mpt3sas_ctl_init(2); //注册/dev/mpt3clt这个设备 #endif /* MPT2SAS_SCSI */ error = pci_register_driver(&mpt3sas_driver); //注册一个pci设备 if (error) scsih_exit(); return error; } static int scsih_init(void) { mpt2_ids = 0; mpt3_ids = 0; mpt3sas_base_initialize_callback_handler(); /* queuecommand callback hander */ scsi_io_cb_idx = mpt3sas_base_register_callback_handler(_scsih_io_done); /* task management callback handler */ tm_cb_idx = mpt3sas_base_register_callback_handler(_scsih_tm_done); /* base internal commands callback handler */ base_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_base_done); port_enable_cb_idx = mpt3sas_base_register_callback_handler( mpt3sas_port_enable_done); /* transport internal commands callback handler */ transport_cb_idx = mpt3sas_base_register_callback_handler( mpt3sas_transport_done); /* scsih internal commands callback handler */ scsih_cb_idx = mpt3sas_base_register_callback_handler(_scsih_done); /* configuration page API internal commands callback handler */ config_cb_idx = mpt3sas_base_register_callback_handler( mpt3sas_config_done); /* ctl module callback handler */ ctl_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_ctl_done); tm_tr_cb_idx = mpt3sas_base_register_callback_handler( _scsih_tm_tr_complete); tm_tr_volume_cb_idx = mpt3sas_base_register_callback_handler( _scsih_tm_volume_tr_complete); tm_sas_control_cb_idx = mpt3sas_base_register_callback_handler( _scsih_sas_control_complete); return 0; }

我们不详细去分析mpt3sas_transport_template、mpt3sas_raid_template和scsih_init到底干了啥,因为我们的目的是了解mpt3sas这个驱动的结构,有个大概的框架,以后需要深入接触的时候再仔细分析。我们重点关注mpt3sas注册的pci设备。 PCI 的注册就是将 PCI 驱动程序挂载到其所在的总线的 drivers 链,同时扫描 PCI 设备,将它能够进行驱动的设备挂载到 driver 上的 devices 链表上并对其调用probe函数进行。mpt3sas的pci probe函数是_scsih_probe

static int _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct MPT3SAS_ADAPTER *ioc; //创建并初始化MPT3SAS_ADAPTER ... /* Use mpt3sas driver host template for SAS 3.0 HBA's */ //申请一个scsi host shost = scsi_host_alloc(&mpt3sas_driver_template, sizeof(struct MPT3SAS_ADAPTER)); if (!shost) return -ENODEV; ioc = shost_priv(shost); ... ioc->is_driver_loading = 1; if ((mpt3sas_base_attach(ioc))) { // 初始化控制器,分配资源 pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); rv = -ENODEV; goto out_attach_fail; } ... rv = scsi_add_host(shost, &pdev->dev); //添加到scsi middle level if (rv) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); goto out_add_shost_fail; } ... scsi_scan_host(shost); //扫描所有scsi总线 return 0; ... }

控制器驱动是这样的套路,先分配资源初始化控制器,然后创建个scsi host添加到系统,然后扫描scsi 总线的设备创建一个个target连接到系统中,此时IO栈看到的就是scsi设备,再往上就是通用块层。IO到了scsi层,怎么再和下面的硬件进行通信的就在这个驱动里实现,但我们不需要关心那么多。

补充来自网上的资料

在 SCSI middle level 定义了 Scsi host、Scsi target、Scsi device 等数据结构。Scsi host 描述了一个 scsi 总线控制器,在很多实际的系统中,scsi host 为一块基于 PCI 总线的 HBA 或一个 SCSI 控制器芯片。每个 Scsi host 中可以存在多个 channel,一个 channel 实际扩展了一条 scsi 总线。每个 channel 可以 连接多个 scsi 节点,具体连接的数量与 scsi 总线带载能力有关。Scsi target 对 scsi 总线上的 scsi node 进行了抽象。每个 Scsi target可能拥有多个 lun,即多个 Scsi device。Scsi device 用于描述一个 scsi 的具体功能单元,其在 scsi host 中通过channel、id、lun 进行寻址。需要注意的是 Scsi device 的作用是扩充了 Scsi target 的描述设备能力,不代表任何实体属性。在实际环境里, Scsi target 对应一个物理磁盘或 Raid卷,Scsi device 对应一个逻辑单元 lun。

以上就是mpt3sas这个控制器驱动的大体结构。 我遇到的宕机问题是/dev/mpt3sas ioctl的处理过程中控制器资源已经被释放了,导致内存非法访问,这个一般是并发导致的,和https://patchwork.kernel.org/patch/10196101/这个patch描述的问题很像,但不是同一个问题。具体原因还在分析中。

参考:

mptsas驱动阅读笔记
转载请注明原文地址: https://www.6miu.com/read-5039300.html

最新回复(0)