本节首先对scsi host相关的结构体做描述,然后介绍scsi host的管理包括scsi host的分配/释放以及添加到系统中。
1. scsi host相关结构体介绍
1.1 scsi_host结构体
当OS通过HBA与硬盘进行交互之前,需要识别当前的HBA,然后才能识别连接到HBA上的硬盘。从SCSI层到LLDD层(有些驱动可能使用libsas层,有些没有),每一层都存在表示HBA的结构体。在SCSI层使用的是结构体scsi host。
结构体Scsi_Host各成员 | ||
__devices | 识别到的scsi device | |
__targets | 识别到的scsi target | |
starved_list | 可能长时间没有得到执行的scsi device | |
default_lock | ||
host_lock | Scsi host锁 | |
scan_mutex | 扫描过程使用锁 | |
eh_abort_list | Scsi abort时放入此链 | |
eh_cmd_q | EH时将异常的命令放入此链 | |
ehandler | 错误处理线程 | |
Eh_action | ||
host_wait | ||
hostt | Scsi_host_template每个驱动需要填充的模板 | |
transportt | Scsi_transport_template 传输层模板,驱动可以覆盖 | |
tag_set | Share tag驱动使用的blk_mq_tag_set | |
host_blocked | 被阻塞的host数目 | |
Host_failed | ||
host_eh_scheduled | 尝试调用EH的次数 | |
host_no | Host id | |
eh_deadline | ||
last_reset | 上次reset时间 | |
max_channel | 支持最大channel | |
max_id | 支持最大ID | |
max_lun | 支持最多lun | |
unique_id | 独一无二的id | |
max_cmd_len | 最大命令长度 | |
This_id | ||
can_queue | 表示队列最大深度 | 与scsi_host_template中相应的成员一样 |
Cmd_per_lun | 表示每个硬盘深度 | |
Sg_tablesize | 表示最大segment数目 | |
Sg_prot_tablesize | 表示最大segment数目(DIF) | |
Max_sectors | 表示IO支持最大sector | |
Max_segment_size | 表示IO支持最大segment的size | |
Dma_boundary | ||
Virt_dma_boundary | ||
Nr_hw_queues | 表示支持的硬件队列数目 | |
Nr_maps | 表示支持的map数目 | |
active_mode | ||
Host_self_blocked | ||
Reserse_ordering | ||
Tmf_in_progress | ||
async_scan | 异步扫描正在进行中 | |
eh_noresume | =1 在错误处理之前不去唤醒scsi host =0 在错误处理前唤醒scsi host | |
No_write_same | ||
Host_tagset | 表示使用share-tag | |
Show_inquiry | ||
No_scsi2_lun_in_cdb | ||
Work_q_name[20] | ||
Work_q | workqueue | |
Tmf_work_q | ||
max_host_blocked | ||
Prot_capabilities | 保护信息的能力 | |
Prot_guard_type | 保护信息的guard类型 | |
base | 目前驱动很少用 | |
Io_port | ||
N_io_port | ||
Dma_channel | ||
irq | ||
Shost_state | Scsi host的状态 | |
shost_gendev | Scsi host对应的gendev | |
shost_dev | Scsi host对应的device | |
Shost_dev_attr_groups[3] | Shost dev对应的属性 | |
Shost_data | 一般用于指示到底层驱动对应的hba结构体 | |
dma_dev | ||
hostdata[] | 可用于存储host相关数据 |
暂时只对常用的成员对简单的解释,比较重要的可分如下几类:
- 底层驱动或硬件的相关限制,如max_sectors/sg_tagblesize/can_queue/nr_hw_queues等;
- Scsi host自身相关,如host_no/shost_data/host_blocked/host_failed/shost_state/lock等;
- Scsi host扫描设备过程中用到的,如__devices/__targets/scan_mute等;
其他还有很多成员,真正用到时再做分析。
1.2 scsi_host_template结构体
结构体Scsi_host_templace是非常重要的结构体,每个SCSI驱动必须定义此结构体,并进行填充。
结构体scsi_host_template各成员 | ||
成员名 | 含义 | 备注 |
cmd_size | 提前分配request/scsi command时可以为底层驱动分配的per-command数据大小 | |
queuecommand | IO下发函数 | 各SCSI驱动需要填充 |
commit_rqs | 暂不分析 | |
module | 模块 | |
name | 名称 | |
info | ||
ioctl() | ||
init_cmd_priv() exit_cmd_priv() | 若cmd_size中定义底层驱动私有结构长度,可以通过此接口初始化私有结构,若未定义此接口默认清0 | |
eh_abort_handler() | SCSI错误处理函数,用于abort一个SCSI命令 | 错误处理使用,逐步升级处理 |
eh_device_reset_handler() | SCSI错误处理函数,用于对device发送device reset | |
eh_target_reset_handler() | SCSI错误处理函数,用于对target发送target reset | |
eh_bus_reset_handler() | SCSI错误处理函数,用于发送bus reset | |
eh_host_reset_handler() | SCSI错误处理函数,用于发送host reset | |
slave_alloc() | 在SCSI device识别过程中调用 | 驱动可以根据当前驱动自定义特定的操作 |
slave_configure() | 在SCSI device识别过程中调用 | |
slave_destroy() | 在SCSI device释放过程中调用 | |
target_alloc() | 在SCSI target识别过程中调用 | 驱动可以根据当前驱动自定义特定的操作 |
target_destroy() | 在SCSI target释放过程中调用 | |
scan_finished() | 判断是否扫描scsi device结束 | |
scan_start() | 开始扫描scsi device | |
change_queue_depth() | 用于修改scsi device的队列深度 | |
map_queue() | 用于驱动将硬件队列映射到BLOCK层 | |
mq_poll() | 用于poll接口 | |
dma_need_drain() | ||
Bios_param | ||
Unlock_native_capacity | ||
Show_info() | 用于将驱动数据和信息导出到用户态 | |
Write_info() | 用于将驱动数据和信息导出到用户态 | |
eh_timed_out() | 可选路径, | |
eh_should_retry_cmd | 用于驱动决定是否重试SCSI命令 | |
host_reset() | 控制器复位接口 | |
Proc_name | Proc名字 | |
Proc_dir | proc目录 | |
can_queue | 硬件队列深度 | |
This_id | ||
sg_tablesize | 每个IO最大segment数目 | |
Sg_prot_tablesize | 每个IO最大segment数目(DIF) | |
max_sectors | 每个IO支持最大sector | |
max_segment_size | 每个segment最大的size | |
dma_boundary | DMA边界 | |
Virt_boundary_mask | ||
cmd_per_lun | 队列深度 | |
present | 在增加proc hostdir时增加,表示有多少个host | |
tag_alloc_policy | 默认不设置为BLK_TAG_ALLOC_FIFO,可以设置为BLK_TAG_ALLOC_RR | |
track_queue_depth | 跟踪QUEUE_FULL事件,减少队列深度 | |
supported_mode | 默认为MODE_INITIATOR | |
emulated | ||
skip_settle_delay | 是否在eh_host_reset_handler之后需要休眠HOST_RESET_SETTLE_TIME秒 | |
no_write_same | 是否控制器支持WRITE SAME | |
host_tagset | 是否为share tag驱动 | |
max_host_blocked | 最大host被blocked次数 | |
shost_groups | Scsi host的sysfs属性相关 | |
sdev_groups | Scsi device的sysfs属性相关 | |
vendor_id | 厂商ID | |
cmd_pool | 当前没有使用,可删除 | |
Rpm_autosuspend_delay | 是否开启scsi device的autosuspend延时,与scsi/block runtime PM相关 |
每个SCSI驱动需要填充结构体scsi_host_template,但并不是每个成员都需要填充,默认不填会使用默认值,根据驱动自身情况进行修改。正如名字一样,它为scsi host的模板,每个驱动需要定义。
2. scsi host的管理
Scsi host代表HBA控制器,SCSI HOST的管理包括SCSI HOST的分配/释放,将SCSI HOST添加到系统以及对SCSI HOST的扫描(此步在下一节介绍)。这三步一般为SCSI驱动进行调用,是SCSI驱动的必须固定步骤。
2.1 scsi host的分配
Scsi host的分配是由函数scsi_host_alloc()实现的,它有两个输入参数:scsi_host_template和privsize。根据scsi_host_template填充scsi host部分成员,privsize是底层驱动让SCSI在分配scsi host时为底层驱动分配的内存,即scsi host->hostdata[]成员。
主要设置如下:
- Host_no,scsi host的id;
- Shost->hostt = scsi_host_template
- 与scsi_host_template一样的参数,如can_queue / sg_tablesize / cmd_per_lun / max_sectors /max_segment_size /dma_boundary等;
- sysfs相关属性;
- 创建错误处理线程shost->ehandler;
- 用于abort SCSI命令的工作队列tmf_work_q;
- 将scsi host 添加到proc文件系统中;
2.2 scsi host的释放
Scsi host的释放是由函数scsi_remove_host()实现的,在这里需要注意的,shost->shost_gendev的unregister将引用计数减为0时,才会调用scsi_host_dev_release()做shost_gendev相关的资源释放。
2.3 scsi host的添加
Scsi host的添加指的是将scsi host添加到系统中,主要通过函数scsi_add_host()实现。过程如下:
- 创建scsi_sense_cache缓冲区;
- 为scsi host分配tag_set,进行相关设置,设置tag_set->ops,并调用blk_mq_alloc_tag_set()提前分配request和tag,对CPU和hctx进行映射(见BLOCK层代码分析(7)IO下发之request的分配和获取);
- 将shost_gendev和shost_dev添加到系统中;
- 将scsi host添加到sysfs文件系统中;
- 将scsi host添加到proc文件系统中;