本节首先对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相关数据


暂时只对常用的成员对简单的解释,比较重要的可分如下几类:

  1. 底层驱动或硬件的相关限制,如max_sectors/sg_tagblesize/can_queue/nr_hw_queues等;
  2. Scsi host自身相关,如host_no/shost_data/host_blocked/host_failed/shost_state/lock等;
  3. 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[]成员。

主要设置如下:

  1. Host_no,scsi host的id;
  2. Shost->hostt = scsi_host_template
  3. 与scsi_host_template一样的参数,如can_queue / sg_tablesize / cmd_per_lun / max_sectors /max_segment_size /dma_boundary等;
  4. sysfs相关属性;
  5. 创建错误处理线程shost->ehandler;
  6. 用于abort SCSI命令的工作队列tmf_work_q;
  7. 将scsi host 添加到proc文件系统中;

2.2 scsi host的释放

        Scsi host的释放是由函数scsi_remove_host()实现的,在这里需要注意的,shost->shost_gendev的unregister将引用计数减为0时,才会调用scsi_host_dev_release()做shost_gendev相关的资源释放。

scsi 架构 scsi层_错误处理

 

2.3 scsi host的添加

        Scsi host的添加指的是将scsi host添加到系统中,主要通过函数scsi_add_host()实现。过程如下:

  1. 创建scsi_sense_cache缓冲区;
  2. 为scsi host分配tag_set,进行相关设置,设置tag_set->ops,并调用blk_mq_alloc_tag_set()提前分配request和tag,对CPU和hctx进行映射(见BLOCK层代码分析(7)IO下发之request的分配和获取);
  3. 将shost_gendev和shost_dev添加到系统中;
  4. 将scsi host添加到sysfs文件系统中;
  5. 将scsi host添加到proc文件系统中;

scsi 架构 scsi层_SCSI HOST_02