Linux内核6.2块驱动的数据持久化实现方法问询
Linux 6.2块设备驱动数据持久化及submit_bio递归问题解答
内存块持久化核心逻辑
要实现内存块到存储设备的持久化,核心是将处理后的读写请求提交给底层真实存储设备,而非停留在内存中,具体步骤如下:
- 持有底层设备引用:驱动初始化阶段,通过
blk_get_dev()打开目标物理块设备(比如存储数据的/dev/sdb、存储奇偶块的/dev/sdc),获取struct block_device *句柄,保存到驱动私有数据结构中。 - 请求处理与转换:在驱动的请求处理函数中(内核6.2推荐使用
make_request_fn,偏向bio级别的高效处理),对收到的bio做XOR冗余处理:- 写操作:计算生成奇偶块的bio,将原始数据bio和奇偶块bio分别指向对应的底层设备;
- 读操作:校验数据完整性,若损坏则用奇偶块恢复后再返回给上层。
- 提交到底层设备:修改处理后bio的
bi_bdev字段为底层设备的struct block_device,提交该bio完成持久化。
避免submit_bio递归的关键
递归的根源是将处理后的bio提交回了自己的驱动设备,只要确保提交目标是底层真实设备,就不会触发递归:
- 绝对不能将bio的
bi_bdev设置为自己驱动创建的gendisk对应的bdev; - 必须指向初始化阶段获取的底层物理块设备的bdev。
代码示例片段
// 驱动私有数据结构 struct my_raid_data { struct block_device *data_bdev; // 存储数据的底层设备 struct block_device *parity_bio; // 存储奇偶块的底层设备 struct gendisk *disk; }; static void my_make_request(struct request_queue *q, struct bio *bio) { struct my_raid_data *raid_data = q->queuedata; struct bio *parity_bio = NULL; // 处理写请求:生成奇偶块 if (bio_op(bio) == REQ_OP_WRITE) { // 自定义函数:基于原始bio生成奇偶块的bio parity_bio = create_parity_bio(bio, raid_data); if (!parity_bio) { bio_io_error(bio); return; } // 设置奇偶块bio的目标设备并提交 parity_bio->bi_bdev = raid_data->parity_bdev; submit_bio(parity_bio); } // 修改原始bio的目标设备为底层数据设备,提交到底层 bio->bi_bdev = raid_data->data_bdev; submit_bio(bio); } // 驱动probe函数中打开底层设备示例 static int my_raid_probe(struct platform_device *pdev) { struct my_raid_data *raid_data = devm_kzalloc(&pdev->dev, sizeof(*raid_data), GFP_KERNEL); // 打开数据设备(示例路径,实际需根据配置获取) raid_data->data_bdev = blk_get_dev("/dev/sdb", FMODE_READ | FMODE_WRITE); if (IS_ERR(raid_data->data_bdev)) return PTR_ERR(raid_data->data_bdev); // 打开奇偶块设备 raid_data->parity_bdev = blk_get_dev("/dev/sdc", FMODE_READ | FMODE_WRITE); if (IS_ERR(raid_data->parity_bdev)) { blk_put_dev(raid_data->data_bdev); return PTR_ERR(raid_data->parity_bdev); } // 初始化gendisk和请求队列,设置make_request_fn raid_data->disk = alloc_disk(1); raid_data->disk->fops = &my_disk_fops; raid_data->disk->queue = blk_alloc_queue(GFP_KERNEL); blk_queue_make_request(raid_data->disk->queue, my_make_request); raid_data->disk->queue->queuedata = raid_data; // 其他gendisk初始化操作... add_disk(raid_data->disk); return 0; }
额外注意事项
- bio生命周期管理:生成的奇偶块bio需在
end_io回调中释放,避免内存泄漏; - IO完成跟踪:若需要保证原始数据和奇偶块的写入原子性,可通过
bio_set_end_io设置回调,在所有关联bio完成后再通知上层; - 内核版本适配:Linux 6.2中,bio操作需使用
bio_op()、bio_for_each_segment()等现代接口,避免使用已废弃的函数; - 错误处理:底层设备IO失败时,需在回调中触发奇偶块修复逻辑,保证数据可用性。
内容的提问来源于stack exchange,提问作者sile
相关产品推荐
相关产品推荐

