SCSI层代码分析(1)SCSI HOST的管理
本节首先对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文件系统中;

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
