Linux kernel block device 的 submit_bio 都做了什么?

本文介绍了Linux内核中submit_bio函数的使用方法及其参数含义。该函数用于提交块设备的读写请求,通过传递rw参数来指定操作类型,并利用bio结构描述具体的I/O请求。关键字段bi_bdev指定了进行读写的设备。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看看原型申明:

void submit_bio(int rw, struct bio *bio)

其中

rw 代表是读还是写

bio 描述这个I/O的结构

最后submit_bio调用generic_make_request(bio);去完成真正的IO请求。

需要注意一点的是,bio结构里面有一个很重要的字段

struct block_device    *bi_bdev;

这个bi_bdev就决定了从哪个设备上去读写数据。

所以,在内核中,如果我们要读取某个block设备的数据,使用submit_bio就可以得到数据的,自己构建一个bio。

这是现在的代码#include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/fs.h> #include <linux/blkdev.h> #include <linux/bio.h> #include <linux/random.h> #include <linux/version.h> #include <linux/slab.h> #define DEV_NAME "sda1" #define START_BYTE (562 * 1024 * 1024) #define END_BYTE (818 * 1024 * 1024) static struct gendisk *target_disk; static struct block_device *target_bdev; static int trigger_enabled = 0; static unsigned long error_count = 0; static struct proc_dir_entry *trigger_proc; static unsigned int logical_block_size = 512; // 多版本API支持 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) typedef blk_qc_t (submit_bio_t)(struct bio *bio); static submit_bio_t *orig_submit_bio; #else typedef blk_qc_t (make_request_fn_t)(struct request_queue *q, struct bio *bio); static make_request_fn_t *orig_make_request_fn; #endif // 创建可修改的fops副本 static struct block_device_operations *modified_fops = NULL; // 处理/proc/trigger_io写入 static ssize_t trigger_io_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char cmd[32]; // 检查用户空间指针有效性 if (!buf) return -EFAULT; // 从用户空间复制数据 if (copy_from_user(cmd, buf, min(count, sizeof(cmd)-1))) return -EFAULT; // 确保字符串终止 cmd[min(count, sizeof(cmd)-1)] = '\0'; // 命令解析 if (strcmp(cmd, "enable") == 0 || strcmp(cmd, "1") == 0) { trigger_enabled = 1; printk(KERN_INFO "I/O error trigger ENABLED\n"); } else if (strcmp(cmd, "disable") == 0 || strcmp(cmd, "0") == 0) { trigger_enabled = 0; printk(KERN_INFO "I/O error trigger DISABLED\n"); } else if (strcmp(cmd, "reset") == 0) { error_count = 0; printk(KERN_INFO "Error counter reset\n"); } else { printk(KERN_WARNING "Unknown command: %s\n", cmd); return -EINVAL; // 无效命令返回错误 } // 必须返回写入的字节数 return count; } // 处理/proc/trigger_io读取 static ssize_t trigger_io_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char status[128]; int len; // 生成状态信息 len = snprintf(status, sizeof(status), "Status: %s\nError count: %lu\nTarget device: %s\nRange: %uMB-%uMB\nSector size: %u\n", trigger_enabled ? "ENABLED" : "DISABLED", error_count, DEV_NAME, START_BYTE/(1024*1024), END_BYTE/(1024*1024), logical_block_size); // 使用内核辅助函数返回数据 return simple_read_from_buffer(buf, count, ppos, status, len); } static const struct proc_ops trigger_io_fops = { .proc_read = trigger_io_read, .proc_write = trigger_io_write, }; // Linux 5.9+ 使用submit_bio API #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) static void modified_submit_bio(struct bio *bio) { if (trigger_enabled && bio->bi_disk && bio->bi_disk == target_disk) { sector_t sector = bio->bi_iter.bi_sector; unsigned long byte_offset = (unsigned long)(sector * logical_block_size); if (byte_offset >= START_BYTE && byte_offset <= END_BYTE) { if (get_random_u32() % 2 == 0) { printk(KERN_INFO "Injecting I/O error at sector %llu\n", (u64)sector); bio->bi_status = BLK_STS_IOERR; bio_endio(bio); error_count++; return; } } } orig_submit_bio(bio); } // Linux <5.9 使用make_request_fn API #else static blk_qc_t modified_make_request(struct request_queue *q, struct bio *bio) { if (trigger_enabled && bio->bi_disk && bio->bi_disk == target_disk) { sector_t sector = bio->bi_iter.bi_sector; unsigned long byte_offset = (unsigned long)(sector * logical_block_size); if (byte_offset >= START_BYTE && byte_offset <= END_BYTE) { if (get_random_u32() % 2 == 0) { printk(KERN_INFO "Injecting I/O error at sector %llu\n", (u64)sector); bio->bi_status = BLK_STS_IOERR; bio_endio(bio); error_count++; return BLK_QC_T_NONE; } } } return orig_make_request_fn(q, bio); } #endif static int __init trigger_io_init(void) { int ret = 0; struct request_queue *q = NULL; // 声明提前 // 获取块设备 target_bdev = blkdev_get_by_dev(MKDEV(8, 1), FMODE_READ, NULL); if (IS_ERR(target_bdev)) { ret = PTR_ERR(target_bdev); printk(KERN_ERR "Failed to get block device for %s, error %d\n", DEV_NAME, ret); return ret; } target_disk = target_bdev->bd_disk; if (!target_disk) { printk(KERN_ERR "Failed to get gendisk for %s\n", DEV_NAME); blkdev_put(target_bdev, FMODE_READ); return -ENODEV; } q = bdev_get_queue(target_bdev); if (!q) { printk(KERN_ERR "Failed to get request queue for %s\n", DEV_NAME); blkdev_put(target_bdev, FMODE_READ); return -ENODEV; } logical_block_size = queue_logical_block_size(q); printk(KERN_INFO "Device %s logical block size: %u\n", DEV_NAME, logical_block_size); // 内核版本适配 (Linux 5.9+) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) // 创建fops的副本 modified_fops = kmemdup(target_disk->fops, sizeof(*modified_fops), GFP_KERNEL); if (!modified_fops) { printk(KERN_ERR "Failed to allocate fops copy\n"); blkdev_put(target_bdev, FMODE_READ); return -ENOMEM; } // 保存原始函数指针 orig_submit_bio = modified_fops->submit_bio; // 修改副本中的函数指针 modified_fops->submit_bio = modified_submit_bio; // 替换整个fops结构 target_disk->fops = modified_fops; #else // Linux <5.9 使用传统方法 orig_make_request_fn = q->make_request_fn; WRITE_ONCE(q->make_request_fn, modified_make_request); #endif // 创建/proc文件 trigger_proc = proc_create("trigger_io", 0644, NULL, &trigger_io_fops); if (!trigger_proc) { printk(KERN_ERR "Failed to create /proc/trigger_io\n"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) target_disk->fops->submit_bio = orig_submit_bio; #else WRITE_ONCE(q->make_request_fn, orig_make_request_fn); #endif blkdev_put(target_bdev, FMODE_READ); return -ENOMEM; } printk(KERN_INFO "I/O trigger module loaded for %s\n", DEV_NAME); return 0; } static void __exit trigger_io_exit(void) { if (target_bdev) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) // 恢复原始fops if (modified_fops) { target_disk->fops = modified_fops->submit_bio; kfree(modified_fops); modified_fops = NULL; } #else // 恢复传统方法 struct request_queue *q = bdev_get_queue(target_bdev); if (q && orig_make_request_fn) { WRITE_ONCE(q->make_request_fn, orig_make_request_fn); } #endif blkdev_put(target_bdev, FMODE_READ); } if (trigger_proc) { proc_remove(trigger_proc); } printk(KERN_INFO "I/O trigger module unloaded\n"); } module_init(trigger_io_init); module_exit(trigger_io_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Controlled I/O error trigger"); 编译报错/k_trigger_io.c:184:35: error: assignment to 'blk_qc_t (*)(struct bio *)' {aka 'unsigned int (*)(struct bio *)'} from incompatible pointer type 'void (*)(struct bio *)' [-Werror=incompatible-pointer-types] 184 | modified_fops->submit_bio = modified_submit_bio; | ^ /home/bba/projects/nvr/repo_reconstruct/torchlight/build_dir/linux-mstar_msr931/tp_k_trigger_io/k_trigger_io.c:199:43: error: assignment of member 'submit_bio' in read-only object 199 | target_disk->fops->submit_bio = orig_submit_bio;
08-08
代码如下#include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/fs.h> #include <linux/blkdev.h> #include <linux/bio.h> #include <linux/random.h> #include <linux/version.h> #include <linux/slab.h> #define DEV_NAME "sda1" #define START_BYTE (562 * 1024 * 1024) #define END_BYTE (818 * 1024 * 1024) static struct gendisk *target_disk; static struct block_device *target_bdev; static int trigger_enabled = 0; static unsigned long error_count = 0; static struct proc_dir_entry *trigger_proc; static unsigned int logical_block_size = 512; // 多版本API支持 typedef blk_qc_t (submit_bio_t)(struct bio *bio); static submit_bio_t *orig_submit_bio; // 修正的函数签名 static blk_qc_t modified_submit_bio(struct bio *bio) { if (trigger_enabled && bio->bi_disk && bio->bi_disk == target_disk) { sector_t sector = bio->bi_iter.bi_sector; unsigned long byte_offset = (unsigned long)(sector * logical_block_size); if (byte_offset >= START_BYTE && byte_offset <= END_BYTE) { if (get_random_u32() % 2 == 0) { printk(KERN_INFO "Injecting I/O error at sector %llu\n", (u64)sector); bio->bi_status = BLK_STS_IOERR; bio_endio(bio); error_count++; return BLK_QC_T_NONE; // 必须返回正确类型 } } } // 调用原始函数并返回正确类型 return orig_submit_bio(bio); } // 创建可修改的fops副本 static const struct block_device_operations *orig_fops = NULL; static struct block_device_operations *modified_fops = NULL; // 处理/proc/trigger_io写入 static ssize_t trigger_io_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char cmd[32]; // 检查用户空间指针有效性 if (!buf) return -EFAULT; // 从用户空间复制数据 if (copy_from_user(cmd, buf, min(count, sizeof(cmd)-1))) return -EFAULT; // 确保字符串终止 cmd[min(count, sizeof(cmd)-1)] = '\0'; // 命令解析 if (strcmp(cmd, "enable") == 0 || strcmp(cmd, "1") == 0) { trigger_enabled = 1; printk(KERN_INFO "I/O error trigger ENABLED\n"); } else if (strcmp(cmd, "disable") == 0 || strcmp(cmd, "0") == 0) { trigger_enabled = 0; printk(KERN_INFO "I/O error trigger DISABLED\n"); } else if (strcmp(cmd, "reset") == 0) { error_count = 0; printk(KERN_INFO "Error counter reset\n"); } else { printk(KERN_WARNING "Unknown command: %s\n", cmd); return -EINVAL; // 无效命令返回错误 } // 必须返回写入的字节数 return count; } // 处理/proc/trigger_io读取 static ssize_t trigger_io_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char status[128]; int len; // 生成状态信息 len = snprintf(status, sizeof(status), "Status: %s\nError count: %lu\nTarget device: %s\nRange: %uMB-%uMB\nSector size: %u\n", trigger_enabled ? "ENABLED" : "DISABLED", error_count, DEV_NAME, START_BYTE/(1024*1024), END_BYTE/(1024*1024), logical_block_size); // 使用内核辅助函数返回数据 return simple_read_from_buffer(buf, count, ppos, status, len); } static const struct proc_ops trigger_io_fops = { .proc_read = trigger_io_read, .proc_write = trigger_io_write, }; // Linux 5.9+ 使用submit_bio API static void modified_submit_bio(struct bio *bio) { if (trigger_enabled && bio->bi_disk && bio->bi_disk == target_disk) { sector_t sector = bio->bi_iter.bi_sector; unsigned long byte_offset = (unsigned long)(sector * logical_block_size); if (byte_offset >= START_BYTE && byte_offset <= END_BYTE) { if (get_random_u32() % 2 == 0) { printk(KERN_INFO "Injecting I/O error at sector %llu\n", (u64)sector); bio->bi_status = BLK_STS_IOERR; bio_endio(bio); error_count++; return; } } } orig_submit_bio(bio); } static int __init trigger_io_init(void) { int ret = 0; struct request_queue *q = NULL; // 声明提前 // 获取块设备 target_bdev = blkdev_get_by_dev(MKDEV(8, 1), FMODE_READ, NULL); if (IS_ERR(target_bdev)) { ret = PTR_ERR(target_bdev); printk(KERN_ERR "Failed to get block device for %s, error %d\n", DEV_NAME, ret); return ret; } target_disk = target_bdev->bd_disk; if (!target_disk) { printk(KERN_ERR "Failed to get gendisk for %s\n", DEV_NAME); blkdev_put(target_bdev, FMODE_READ); return -ENODEV; } q = bdev_get_queue(target_bdev); if (!q) { printk(KERN_ERR "Failed to get request queue for %s\n", DEV_NAME); blkdev_put(target_bdev, FMODE_READ); return -ENODEV; } logical_block_size = queue_logical_block_size(q); printk(KERN_INFO "Device %s logical block size: %u\n", DEV_NAME, logical_block_size); // 内核版本适配 (Linux 5.9+) // 保存原始fops指针 orig_fops = target_disk->fops; // 创建fops的副本 modified_fops = kmemdup(orig_fops, sizeof(*orig_fops), GFP_KERNEL); if (!modified_fops) { printk(KERN_ERR "Failed to allocate fops copy\n"); blkdev_put(target_bdev, FMODE_READ); return -ENOMEM; } // 保存原始函数指针 orig_submit_bio = orig_fops->submit_bio; // 修改副本中的函数指针 modified_fops->submit_bio = modified_submit_bio; // 安全替换整个fops结构 rcu_assign_pointer(target_disk->fops, modified_fops); // 创建/proc文件 trigger_proc = proc_create("trigger_io", 0644, NULL, &trigger_io_fops); if (!trigger_proc) { printk(KERN_ERR "Failed to create /proc/trigger_io\n"); target_disk->fops->submit_bio = orig_submit_bio; blkdev_put(target_bdev, FMODE_READ); return -ENOMEM; } printk(KERN_INFO "I/O trigger module loaded for %s\n", DEV_NAME); return 0; } static void __exit trigger_io_exit(void) { if (target_bdev) { // 恢复原始fops if (orig_fops) { rcu_assign_pointer(target_disk->fops, orig_fops); } // 释放修改后的fops副本 if (modified_fops) { kfree(modified_fops); modified_fops = NULL; } blkdev_put(target_bdev, FMODE_READ); } if (trigger_proc) { proc_remove(trigger_proc); } printk(KERN_INFO "I/O trigger module unloaded\n"); } module_init(trigger_io_init); module_exit(trigger_io_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Controlled I/O error trigger"); 编译报错k_trigger_io.c:119:13: error: conflicting types for 'modified_submit_bio' 119 | static void modified_submit_bio(struct bio *bio) | ^~~~~~~~~~~~~~~~~~~ /home/bba/projects/nvr/repo_reconstruct/torchlight/build_dir/linux-mstar_msr931/tp_k_trigger_io/k_trigger_io.c:28:21: note: previous definition of 'modified_submit_bio' was here 28 | static blk_qc_t modified_submit_bio(struct bio *bio) | ^~~~~~~~~~~~~~~~~~~ /home/bba/projects/nvr/repo_reconstruct/torchlight/build_dir/linux-mstar_msr931/tp_k_trigger_io/k_trigger_io.c: In function 'trigger_io_init': /home/bba/projects/nvr/repo_reconstruct/torchlight/build_dir/linux-mstar_msr931/tp_k_trigger_io/k_trigger_io.c:185:35: error: assignment to 'blk_qc_t (*)(struct bio *)' {aka 'unsigned int (*)(struct bio *)'} from incompatible pointer type 'void (*)(struct bio *)' [-Werror=incompatible-pointer-types] 185 | modified_fops->submit_bio = modified_submit_bio; | ^ /home/bba/projects/nvr/repo_reconstruct/torchlight/build_dir/linux-mstar_msr931/tp_k_trigger_io/k_trigger_io.c:195:40: error: assignment of member 'submit_bio' in read-only object 195 | target_disk->fops->submit_bio = orig_submit_bio; | ^
最新发布
08-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Simple-Soft

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值