SCST基础概念深度解析
概述
SCST (SCSI Target Subsystem for Linux) 是Linux内核中用于构建SCSI目标设备(Target)的高性能框架。它允许Linux系统将本地存储资源通过各种SCSI传输协议(如iSCSI、FC、SRP等)导出给远程主机使用。本文将深入解析SCST的核心概念、架构设计和关键数据结构。
SCST架构概览
SCST采用分层架构设计,主要包含三个核心层次:
graph TB
subgraph "Target Drivers Layer"
A[iSCSI Target]
B[FC Target]
C[SRP Target]
D[User Space Target]
end
subgraph "SCST Core Layer"
E[Command Processing]
F[Session Management]
G[Device Management]
H[Memory Management]
end
subgraph "Device Handlers Layer"
I[VDISK Handler]
J[SCST_USER Handler]
K[Pass-through Handler]
L[CDROM Handler]
end
subgraph "Backend Storage"
M[Block Devices]
N[Files]
O[User Space Programs]
end
A --> E
B --> E
C --> E
D --> E
E --> I
E --> J
E --> K
E --> L
I --> M
I --> N
J --> O
K --> M
L --> M
三层架构详解
Target Drivers Layer (目标驱动层)
- 负责具体传输协议的实现(iSCSI、FC、SRP等)
- 处理协议特定的连接管理和数据传输
- 将接收到的SCSI命令转换为SCST内部格式
SCST Core Layer (核心层)
- 命令处理引擎:管理命令的完整生命周期
- 会话管理:维护initiator与target之间的会话
- 设备管理:管理导出的SCSI设备
- 内存管理:高效的scatter-gather缓冲区管理
Device Handlers Layer (设备处理层)
- 实现具体的SCSI设备类型逻辑
- VDISK:将文件或块设备模拟为SCSI磁盘
- SCST_USER:允许用户空间程序实现SCSI设备
- Pass-through:直接将命令传递给后端SCSI设备
核心数据结构
1. scst_cmd - SCSI命令结构
scst_cmd是SCST中最核心的数据结构,代表一个正在处理的SCSI命令:
1 | struct scst_cmd { |
关键字段说明:
state:命令当前所处的状态机状态,驱动整个命令处理流程cmd_ref:引用计数器,防止命令在异步处理过程中被过早释放sg/sg_cnt:scatter-gather列表,支持高效的零拷贝数据传输tgt_dev:关联的目标设备,连接命令与具体的后端设备
2. scst_tgt - Target结构
scst_tgt代表一个SCSI Target端口:
1 | struct scst_tgt { |
设计要点:
- 一个target可以有多个session(多个initiator连接)
- 通过ACG(Access Control Group)实现细粒度的访问控制
sg_tablesize限制单个I/O的scatter-gather条目数量
3. scst_session - 会话结构
scst_session代表initiator与target之间的一个会话:
1 | struct scst_session { |
会话生命周期:
scst_register_session()- 注册会话- 命令处理阶段 - initiator通过会话发送命令
scst_unregister_session()- 注销会话,清理资源
4. scst_dev - 设备结构
scst_dev代表一个SCSI逻辑设备:
1 | struct scst_dev { |
设备类型:
TYPE_DISK(0) - 直接访问块设备TYPE_TAPE(1) - 顺序访问设备TYPE_CDROM(5) - CD-ROM设备TYPE_PROCESSOR(3) - 处理器设备
命令状态机
SCST使用复杂的状态机来管理命令处理流程。状态分为”主动状态”(active states)和”被动状态”(passive states):
1 | enum scst_cmd_state { |
状态转换示例(READ命令):
stateDiagram-v2
[*] --> INIT_WAIT: 命令到达
INIT_WAIT --> INIT: scst_cmd_init_done()
INIT --> PARSE: LUN解析完成
PARSE --> PREPARE_SPACE: CDB解析完成
PREPARE_SPACE --> TGT_PRE_EXEC: 缓冲区分配完成
TGT_PRE_EXEC --> EXEC_CHECK_SN: 预执行完成
EXEC_CHECK_SN --> REAL_EXEC: 序列号检查通过
REAL_EXEC --> EXEC_WAIT: 提交I/O
EXEC_WAIT --> DEV_DONE: I/O完成
DEV_DONE --> PRE_XMIT_RESP: 设备处理完成
PRE_XMIT_RESP --> XMIT_RESP: 检查通过
XMIT_RESP --> XMIT_WAIT: 开始传输
XMIT_WAIT --> FINISHED: 传输完成
FINISHED --> [*]
执行上下文(Execution Context)
SCST支持三种执行上下文,以平衡性能和灵活性:
1 | enum scst_exec_context { |
上下文选择策略:
DIRECT - 最快,但有限制:
- 不能睡眠
- 不能执行可能阻塞的操作
- 适合简单的命令处理路径
TASKLET - 软中断上下文:
- 延迟执行但仍在中断上下文
- 不能睡眠
- 适合需要延迟但不阻塞的操作
THREAD - 最灵活:
- 可以睡眠和执行阻塞操作
- 可以进行复杂的I/O操作
- 性能开销最大
示例代码:
1 | /* 设备处理器可以返回SCST_CMD_STATE_NEED_THREAD_CTX */ |
内存管理 - SGV Pool
SCST使用SGV(Scatter-Gather Vector)池来高效管理内存:
1 | struct sgv_pool { |
工作原理:
分配时:
- 首先尝试从相应大小的缓存中获取
- 如果缓存为空,则分配新的sgv_pool_obj
- 更新hit/miss统计
释放时:
- 将sgv_pool_obj放回相应的recycling_list
- 避免频繁的内存分配/释放
刷新时(我们刚修复的死锁问题相关):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void sgv_pool_flush(struct sgv_pool *pool)
{
for (i = 0; i < pool->max_caches; i++) {
spin_lock_bh(&pool->sgv_pool_lock);
while (!list_empty(&pool->recycling_lists[i])) {
obj = list_first_entry(&pool->recycling_lists[i],
struct sgv_pool_obj, recycling_list_entry);
__sgv_purge_from_cache(obj);
spin_unlock_bh(&pool->sgv_pool_lock);
sgv_dtor_and_free(obj);
spin_lock_bh(&pool->sgv_pool_lock);
}
spin_unlock_bh(&pool->sgv_pool_lock);
}
}
Target驱动接口
Target驱动通过scst_tgt_template结构向SCST注册:
1 | struct scst_tgt_template { |
关键回调:
xmit_response()- 将SCSI响应发送给initiatorrdy_to_xfer()- 通知initiator可以发送WRITE数据on_abort_cmd()- 处理命令中止请求
设备处理器接口
设备处理器通过scst_dev_type结构注册:
1 | struct scst_dev_type { |
性能优化设计
1. Per-CPU命令队列
1 | struct scst_cmd_threads { |
优势:
- 减少锁竞争
- 提升CPU缓存命中率
- 支持NUMA架构
2. 命令引用计数
1 | static inline void cmd_get(struct scst_cmd *cmd) |
作用:
- 防止命令在异步处理中被过早释放
- 支持多个模块同时引用同一命令
3. 零拷贝数据传输
使用scatter-gather列表直接映射用户页或块设备页:
1 | /* 直接映射块设备页 */ |
总结
SCST通过精心设计的分层架构、高效的数据结构和灵活的状态机,提供了一个高性能、可扩展的SCSI Target框架:
- 分层设计 - 清晰分离传输协议、核心逻辑和设备处理
- 状态机驱动 - 精确控制命令处理流程
- 多上下文支持 - 平衡性能与灵活性
- 高效内存管理 - SGV池减少分配开销
- 可扩展接口 - 支持各种传输协议和设备类型
理解这些基础概念是深入研究SCST实现和进行性能优化的关键。在下一篇文章中,我们将详细分析SCST的命令处理流程和状态转换机制。
参考资料
- SCST源代码:
scst/include/scst.h - SCST核心实现:
scst/src/scst_main.c - Linux SCSI子系统文档:
Documentation/scsi/