块设备驱动故障处理案例
案例 1:磁盘注册后无法挂载
现象:add_disk() 成功,lsblk 能看到设备,但 mount 报错。
排查:
bash
# 检查分区表
fdisk -l /dev/myblk
# 检查文件系统
file -s /dev/myblk
# 查看内核日志
dmesg | grep myblk常见原因:
set_capacity()设置的扇区数与实际不符- 逻辑块大小设置错误(
blk_queue_logical_block_size) - 设备数据区未初始化(ramdisk 需要
vzalloc清零)
案例 2:I/O 超时
现象:dmesg 出现 I/O error 或 blk_update_request: I/O error。
排查:
bash
# 查看错误统计
cat /sys/block/myblk/stat
# 查看队列状态
cat /sys/block/myblk/queue/nr_requests常见原因:
blk_mq_end_request()未在超时前调用- 硬件中断未触发(中断号配置错误)
- DMA 传输未完成
解决方法:
c
/* 实现超时处理 */
static enum blk_eh_timer_return my_timeout(struct request *rq)
{
/* 重置硬件,重新提交请求 */
reset_hardware();
return BLK_EH_RESET_TIMER; /* 重置超时计时器 */
/* 或 BLK_EH_DONE 表示已处理完成 */
}
static const struct blk_mq_ops my_mq_ops = {
.queue_rq = my_queue_rq,
.timeout = my_timeout,
};案例 3:性能低下
现象:fio 测试 IOPS 远低于预期。
排查:
bash
# 查看 I/O 统计
iostat -x 1
# 查看调度器队列深度
cat /sys/block/myblk/queue/nr_requests
# 检查是否开启了合并
cat /sys/block/myblk/queue/nomerges优化方向:
c
/* 增加队列深度 */
tag_set.queue_depth = 1024;
/* 多硬件队列 */
tag_set.nr_hw_queues = num_online_cpus();
/* 对 SSD 禁用旋转标志 */
blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue);
/* 对 NVMe 禁用合并(硬件自己做) */
blk_queue_flag_set(QUEUE_FLAG_NOMERGES, disk->queue);案例 4:内核崩溃于 bio 处理
现象:处理 bio_vec 时触发 NULL 指针或越界访问。
错误代码:
c
/* ❌ 直接访问 bio,忽略 iter */
struct bio_vec *bvec = bio->bi_io_vec;
void *buf = page_address(bvec->bv_page); /* 可能越界 */正确代码:
c
/* ✅ 使用 rq_for_each_segment 安全遍历 */
struct bio_vec bvec;
struct req_iterator iter;
rq_for_each_segment(bvec, rq, iter) {
void *buf = page_address(bvec.bv_page) + bvec.bv_offset;
size_t len = bvec.bv_len;
/* 处理 buf[0..len-1] */
}