这是现在的代码#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;