Linux内核块设备驱动调用add_disk时空指针解引用问题排查
排查Linux 6.2.0块设备驱动add_disk空指针解引用(elv_register_queue触发)
针对加载块设备驱动时add_disk()触发空指针、错误落在elv_register_queue()的问题,核心原因基本是请求队列(request_queue)或其关联结构未正确初始化,以下是具体定位和排查步骤:
1. 检查请求队列的创建流程
Linux 6.2.0推荐使用多队列接口blk_mq,若仍用旧接口需确认初始化完整性:
- 若用
blk_alloc_queue():必须传入合法的请求处理函数,该函数会自动初始化队列内部核心成员(包括elevator、kobj等),禁止手动分配struct request_queue结构体后直接使用。 - 若用
blk_mq系列接口:需确保struct blk_mq_tag_set的nr_hw_queues、queue_depth等参数配置合理,依次调用blk_mq_alloc_tag_set()和blk_mq_init_queue()完成初始化,缺任何一步都会导致队列结构不完整。
2. 确认gendisk与队列的绑定
检查struct gendisk的queue成员是否被正确赋值:
disk->queue = my_initialized_queue;
这一步是add_disk()能正确关联队列的关键,遗漏会导致内核在调用elv_register_queue()时访问空指针。
3. 验证I/O调度器相关初始化
elv_register_queue()会访问队列的elevator成员,需确保该结构已初始化:
- 若未指定自定义调度器,可显式调用
blk_queue_set_default_elevator(my_queue)让内核自动初始化默认调度器; - 禁止手动修改
queue->elevator指针,避免破坏内核初始化的结构。
4. 内核级调试手段
- 打印指针排查:在
my_block_init()中添加printk输出关键指针地址,比如:
加载模块后通过printk(KERN_INFO "my_queue addr: %p, queue->elevator: %p, disk->queue: %p\n", my_queue, my_queue->elevator, disk->queue);dmesg查看,直接确认哪个指针为空。 - 开启内核调试选项:开启
CONFIG_DEBUG_KERNEL、CONFIG_DEBUG_SLAB和CONFIG_STACKTRACE重新编译内核,触发错误时会生成更详细的栈回溯和内存校验信息,精准定位未初始化的成员。 - 分析内核转储:若触发Oops生成了
vmcore,用crash工具加载转储文件,执行bt查看完整调用栈,再通过struct request_queue的结构定义(可通过ptype request_queue查看)检查空指针对应的具体成员。
5. 核对内核6.2.0的接口变更
块设备子系统在6.2版本有部分接口调整,比如旧的blk_init_queue()已被标记为废弃,若沿用5.x及更早版本的初始化代码,可能因接口不兼容导致结构未正确初始化,建议参考内核源码中drivers/block/loop.c等官方块设备驱动的初始化流程对比验证。
内容的提问来源于stack exchange,提问作者Hantong Liu
相关产品推荐
相关产品推荐

