Skip to content

块设备驱动故障处理案例

案例 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 errorblk_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] */
}

褚成志的笔记