Linux内核SCSI错误处理实战:当你的硬盘IO卡住或报错时,内核到底在悄悄做什么?
Linux内核SCSI错误处理实战:当硬盘IO卡住时内核的"侦探工作流"
当服务器日志突然蹦出一条"SCSI error: host 0 channel 0 id 0 lun 0 sense key Not Ready"的警告时,作为运维工程师的你,是否好奇过内核在后台默默执行了哪些"抢救措施"?这就像一场精心设计的侦探剧——从错误触发到恢复,内核的SCSI错误处理机制构建了一套缜密的"破案流程"。
1. 案发现场:SCSI错误的两种典型形态
想象一下,你正在指挥一队快递员(SCSI命令)从仓库(存储设备)取货。突然出现了两种异常情况:
- 快递员失联:命令发出后如同石沉大海,直到超时都未收到回复(IO timeout)
- 快递员带回坏消息:设备返回了包含错误状态码的响应(CHECK CONDITION)
内核通过scsi_decide_disposition()函数对这两种情况进行初步"验伤":
/* 典型的状态判断逻辑 */
switch (result) {
case DID_OK: // 完全成功
if (good_bytes < scsi_bufflen(scmd))
return NEEDS_RETRY; // 部分传输需要重试
break;
case DID_TIME_OUT: // 超时案件
return FAILED;
case DID_ERROR: // 明确错误
return parse_sense_key(scmd); // 解析sense key
}
关键决策点在于错误处理并非立即启动。只有当满足以下条件时才会激活"侦探小组"(SCSI Error Handler):
- 该主机上所有未完成IO要么超时要么异常返回
- 通过
scsi_eh_scmd_add()将问题命令加入调查队列 - 累计错误达到阈值(避免频繁处理零星错误)
提示:可通过
/sys/class/scsi_host/hostX/eh_deadline调整错误处理超时时间(单位秒)
2. 侦探小组的装备箱:错误处理的分级策略
现代SCSI子系统采用类似急诊室"分诊机制"的渐进式恢复策略,其核心哲学是:用最小代价解决问题。就像医生不会对每个感冒患者都开CT检查一样,内核也遵循这样的升级路径:
| 恢复等级 | 操作类型 | 影响范围 | 典型耗时 | 适用场景 |
|---|---|---|---|---|
| 1级 | ABORT/QUERY | 单个命令 | 毫秒级 | 命令卡在设备队列 |
| 2级 | LUN复位 | 单个LUN | 10-100ms | LUN逻辑单元异常 |
| 3级 | 链路复位(I_T_Nexus) | 设备连接 | 100ms级 | HBA与设备通信故障 |
| 4级 | 端口复位 | 整个端口 | 秒级 | 物理链路问题 |
| 5级 | 主机控制器复位 | 整个HBA | 数秒 | 控制器固件挂死 |
在LIBSAS驱动的实现中,这个流程体现在sas_scsi_recover_host()的决策树中:
// 简化的处理流程
if (task_abort_success(scmd)) {
finish_cmd(scmd); // 成功中止命令
} else if (task_status == TASK_IS_AT_LUN) {
sas_recover_lun(dev); // 升级到LUN复位
} else {
sas_phy_reset(phy); // 尝试链路复位
if (failed) {
sas_port_reset(port); // 再升级到端口复位
}
}
实战技巧:通过内核日志可以观察这个升级过程:
$ dmesg | grep -i sas
[ +0.000103] sd 0:0:0:0: [sda] tag#0 ABORT operation started
[ +0.342897] sd 0:0:0:0: [sda] tag#0 ABORT operation failed
[ +0.000011] sas: lldd_I_T_nexus_reset for phy1 initiated
[ +0.120543] sas: I_T nexus reset for phy1 succeeded
3. 物证分析:Sense Key的解码艺术
当设备返回CHECK CONDITION时,Sense Key就是关键的"指纹证据"。熟练解读这些代码能快速定位问题根源:
-
0x02:NOT READY
- 常见原因:设备未完成初始化、介质未就绪
- 典型动作:等待+重试
-
0x03:MEDIUM ERROR
- 常见原因:物理坏块、校验失败
- 典型动作:标记坏块或触发介质扫描
-
0x04:HARDWARE ERROR
- 常见原因:控制器或固件故障
- 典型动作:尝试复位操作
-
0x05:ILLEGAL REQUEST
- 常见原因:无效命令参数
- 典型动作:终止请求并记录
通过sg_logs工具可以直接获取这些关键信息:
$ sg_logs --sense /dev/sda
>> Sense key: Hardware Error
>> Additional sense: Internal target failure
>> Field replaceable unit code: 0x1A
高级技巧:某些HBA卡支持自动重试策略配置(以LSI MegaRAID为例):
$ storcli /c0 show all | grep -A5 "Retry Count"
Retry Count = 8
PD Retry Count = 5
Phy Error Retry = 5
Link Down Retry = 6
4. 现场重建:模拟真实故障的实验方法
理解理论最好的方式就是亲手触发错误。以下是几种安全的实验方法:
4.1 模拟命令超时
使用sg_turs工具人为制造设备无响应:
# 设置超时时间为5秒
$ sg_turs --timeout=5 /dev/sdb
>> sense key: Timeout
>> Transport: retries exhausted
观察内核行为:
$ watch -n 0.1 "cat /proc/scsi/scsi | grep -A3 'Host: scsi0'"
4.2 注入介质错误
通过SCSI DEBUG设备模拟坏块:
# 加载调试驱动
$ modprobe scsi_debug dev_size_mb=1000 opts=2 no_lun=0
# 查看生成的设备
$ lsscsi
[6:0:0:0] disk Linux scsi_debug 0004 /dev/sdc
# 写入触发错误
$ dd if=/dev/urandom of=/dev/sdc bs=1M count=10
>> sd 6:0:0:0: [sdc] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
4.3 跟踪错误处理流程
使用systemtap动态追踪错误处理线程:
probe kernel.function("scsi_error_handler") {
printf("Error handler activated for host%d\n", $shost->host_no)
}
probe kernel.function("sas_scsi_recover_host") {
printf("Recovery level: %d\n", $recovery_level)
}
5. 性能与可靠性的平衡艺术
在真实的存储系统中,错误处理策略需要微妙的平衡:
超时参数的黄金法则:
- 普通SAS硬盘:建议默认超时30秒
- SSD/NVMe设备:可缩短至5-10秒
- 磁带设备:可能需要延长至数分钟
重试策略的优化点:
# 伪代码:智能退避算法示例
def calculate_retry_delay(attempt):
base_delay = 1.0 # 基础延迟1秒
max_delay = 30.0 # 最大延迟30秒
return min(base_delay * (2 ** attempt), max_delay)
监控指标关注点:
/sys/class/scsi_host/hostX/stats中的复位计数iostat -x中的await和svctm异常波动- SMART日志中的硬件错误计数
在云环境中,我们曾遇到过一个典型案例:某批次的NVMe盘在特定负载下会出现微秒级的链路抖动。通过将eh_deadline从默认的60秒调整为5秒,并将链路复位前的重试次数从3次降为1次,成功将故障恢复时间从分钟级缩短到秒级。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)