SCSI子系统基础学习笔记 - 4.scsi_probe

本文详细介绍了Linux内核中SCSI子系统的框架,特别是SCSI磁盘驱动的初始化和探测过程。从init_sd函数开始,讲解了如何注册SCSI设备驱动,以及scsi_probe和sd_probe函数在设备探测中的作用。sd_probe是SCSI磁盘驱动的关键入口,负责创建scsi_disk和gendisk结构,初始化设备,并根据SCSI设备类型进行相应的设置。通过对gendisk和scsi_device的关联,建立了SCSI设备与Linux系统的连接。

1. 前言

本专题我们开始学习SCSI子系统的相关内容。本专题主要参考了《存储技术原理分析》、ULA、ULK的相关内容。本专题主要以硬件UFS为例,记录SCSI子系统的框架流程。

驱动通过消息传递机制获得的SCSI设备最终都被添加到系统中。实际上,它被挂接在SCSI总线类型(scsi_bus_type),实际代码参考分配SCSI设备描述符时,调用的scsi_alloc_sdev->scsi_sysfs_device_initialize函数。
接下来,要真正用上这些设备,就需要高层驱动,这些高层驱动挂载SCSI总线类型,在被加载时,扫描并认领总线类型上未被绑定的SCSI设备。
SCSI设备属于sdev_class类,Linux SCSI子系统实现了sg(SCSI通用接口)和ses(SCSI磁盘柜服务)接口,注册到这个类下。他们都相当于类特定的高层驱动。

上一节在scsi设备探测时讲到scsi_sysfs_add_sdev(sdev)执行此函数将会添加scsi_device,引发devcice_add(scsi_device->device),而sd_init->scsi_register_driver时会执行driver_add,匹配成功将执行 sd_probe,从此处可以看出每一个探测到的scsi_devei也就是每个逻辑单元LU都会执行一次sd_probe,本文就说明一下sd_probe的执行过程。

kernel版本:5.10
平台:arm64

注:
为方便阅读,正文标题采用分级结构标识,每一级用一个"-“表示,如:两级为”|- -", 三级为”|- - -“

2. SCSI驱动模型

在这里插入图片描述

  • scsi_device
    给出了作为SCSI设备方面的属性
  • scsi_disk
    给出了作为SCSI磁盘方面的属性,它作为SCSI设备的代表与上层IO关联
  • gendisk
    给出了作为通用磁盘方面的属性
    这三个结构之间相互联系,通过任何一个结构,可以找到另外两个结构。

在SCSI设备被发现(scsi_scan_host),或者SCSI(高层)驱动被加载(init_sd->scsi_register_driver)时,会在SCSI总线类型上和驱动链表中驱动或者设备链表中的设备尝试绑定。在SCSI总线类型(scsi_bus_type,文件drivers/scsi/scsi_sysfs.c)中定义的match回调函数将首先被调用,这个匹配函数就是scsi_bus_match。SCSI总线类型将设备和驱动的匹配留给驱动来做。

如果总线类型的match回调函数匹配成功,接下来会调用总线类型或驱动的probe回调函数。对于SCSI总线没有定义probe回调,因此会调用scsi_driver的probe,也就是sd_probe。

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

3. init_sd

static int __init init_sd(void)
	|--for (i = 0; i < SD_MAJORS; i++)
	|		if (register_blkdev(sd_major(i), "sd") != 0)
	|			continue;
	|		majors++;
	|		blk_register_region(sd_major(i), SD_MINORS, NULL,sd_default_probe, NULL, NULL); 
	|  //注册scsi_disk类
	|--class_register(&sd_disk_class);
	|--sd_cdb_cache = kmem_cache_create("sd_ext_cdb", SD_EXT_CDB_SIZE,0, 0, NULL);
	<
在 Linux 内核源代码中查找 `scsi_set_device_data` 的定义或使用,可以通过以下几种方式进行: ### 1. 使用 `grep` 或 `find` 命令进行源码搜索 在内核源码目录下,可以使用如下命令来查找包含 `scsi_set_device_data` 的文件: ```bash grep -rn "scsi_set_device_data" /path/to/linux-source/ ``` 这将递归地搜索所有包含该函数的源文件,并显示其所在的行号。通常情况下,这类函数定义会在 SCSI 子系统的头文件中出现,例如 `include/scsi/scsi_device.h`。 ### 2. 查看 `include/scsi/scsi_device.h` 文件 `scsi_set_device_data()` 是一个静态内联函数,用于设置 SCSI 设备的私有数据指针。它的定义可以在 `include/scsi/scsi_device.h` 中找到: ```c static inline void scsi_set_device_data(struct scsi_device *sdev, void *data) { sdev->hostdata = data; } ``` 此函数通过将传入的指针赋值给 `sdev->hostdata` 来实现设备私有数据的绑定[^1]。这种方式使得每个 SCSI 设备可以关联其特定于驱动程序的数据结构,例如 `struct scsi_disk`。 ### 3.SCSI 驱动中查找调用点 在实际使用中,`scsi_set_device_data()` 被 SCSI 驱动广泛用于初始化过程中绑定私有数据结构。例如,在 `sd_probe()` 函数(SCSI驱动)中可以看到如下代码片段: ```c struct scsi_disk *sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL); if (!sdkp) return -ENOMEM; scsi_set_device_data(sdev, sdkp); ``` 这段代码创建了一个 `scsi_disk` 实例,并将其存储在 `sdev->hostdata` 中。后续可以通过 `scsi_get_device_data()` 接口来获取这个指针,从而访问设备相关的私有数据[^1]。 ### 4. 使用 `ctags` 或 IDE 工具辅助导航 如果使用支持符号跳转的开发工具(如 VSCode、CLion、Eclipse CDT),可以快速定位 `scsi_set_device_data()` 的定义和所有引用位置。只需右键点击函数名并选择“Go to Definition”即可跳转到定义处。 此外,也可以手动构建 tags 文件以便快速导航: ```bash ctags -R . ``` 然后使用 Vim 打开源文件,并输入 `:tag scsi_set_device_data` 即可跳转至定义位置。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值