#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#define DEVICE_NAME "blk_test"
#define DEVICE_SIZE (1024 * 1024 * 10) // 10MB
#define SECTOR_SHIFT 9
#define SECTOR_SIZE (1 << SECTOR_SHIFT)
static struct blk_test_device {
struct gendisk *disk;
struct request_queue *queue;
void *data;
sector_t capacity;
} test_dev;
static void blk_test_request( struct request_queue *q )
{
struct request *rq;
printk(KERN_INFO "blk_test: blk_test_request\n");
while ((rq = blk_fetch_request(q)) != NULL) {
sector_t sector = blk_rq_pos(rq);
struct req_iterator iter;
struct bio_vec bvec;
if (blk_rq_pos(rq) + blk_rq_sectors(rq) > test_dev.capacity) {
__blk_end_request_all(rq, -EIO);
continue;
}
if (rq->cmd_type != REQ_TYPE_FS) {
__blk_end_request_all(rq, -EIO);
continue;
}
// 使用兼容的请求段迭代方式
rq_for_each_segment(bvec, rq, iter) {
void *buffer = page_address(bvec.bv_page) + bvec.bv_offset;
unsigned long len = bvec.bv_len;
if (rq_data_dir(rq) == READ)
memcpy(buffer, test_dev.data + (sector << SECTOR_SHIFT), len);
else
memcpy(test_dev.data + (sector << SECTOR_SHIFT), buffer, len);
sector += (len >> SECTOR_SHIFT);
}
__blk_end_request_all(rq, 0);
}
}
static int blk_test_open(struct block_device *bdev, fmode_t mode)
{
printk(KERN_INFO "blk_test: device opened\n");
return 0;
}
static void blk_test_release(struct gendisk *disk, fmode_t mode)
{
printk(KERN_INFO "blk_test: device released\n");
}
static const struct block_device_operations blk_test_fops = {
.owner = THIS_MODULE,
.open = blk_test_open,
.release = blk_test_release,
};
static int __init blk_test_init(void)
{
int ret = 0;
// 分配内存空间
test_dev.data = vmalloc(DEVICE_SIZE);
if (!test_dev.data) {
printk(KERN_ERR "blk_test: failed to allocate memory\n");
return -ENOMEM;
}
memset(test_dev.data, 0, DEVICE_SIZE);
test_dev.capacity = DEVICE_SIZE >> SECTOR_SHIFT;
// 初始化请求队列
test_dev.queue = blk_init_queue(blk_test_request, NULL);
if (!test_dev.queue) {
ret = -ENOMEM;
goto out_vfree;
}
blk_queue_logical_block_size(test_dev.queue, SECTOR_SIZE);
blk_queue_physical_block_size(test_dev.queue, SECTOR_SIZE);
// 分配gendisk结构
test_dev.disk = alloc_disk(1);
if (!test_dev.disk) {
ret = -ENOMEM;
goto out_cleanup_queue;
}
// 动态分配主设备号
int major = register_blkdev(0, "blk_test");
test_dev.disk->major = major;
// 设置gendisk属性
// test_dev.disk->major = 0; // 动态分配主设备号
test_dev.disk->first_minor = 0;
test_dev.disk->fops = &blk_test_fops;
test_dev.disk->private_data = &test_dev;
test_dev.disk->queue = test_dev.queue;
snprintf(test_dev.disk->disk_name, DISK_NAME_LEN, DEVICE_NAME);
//snprintf(test_dev.disk->disk_name, DISK_NAME_LEN, "blk_test_%d", unique_id);
// 设置容量
set_capacity(test_dev.disk, test_dev.capacity);
// 添加磁盘到系统
add_disk(test_dev.disk);
printk(KERN_INFO "\n\n\n************************************************\n");
printk(KERN_INFO "blk_test: module loaded, device size: %lu sectors\n",
(unsigned long)test_dev.capacity);
return 0;
out_cleanup_queue:
blk_cleanup_queue(test_dev.queue);
out_vfree:
vfree(test_dev.data);
return ret;
}
//# 写入测试
//dd if=/dev/zero of=/dev/blk_test bs=1M count=5
//# 读取测试
//dd if=/dev/blk_test of=/dev/null bs=1M count=5
static void __exit blk_test_exit(void)
{
if (test_dev.disk) {
del_gendisk(test_dev.disk);
put_disk(test_dev.disk);
}
if (test_dev.queue) {
blk_cleanup_queue(test_dev.queue);
}
if (test_dev.data) {
vfree(test_dev.data);
}
unregister_blkdev(test_dev.disk->major, "blk_test"); // 新增
}
//module_init(blk_test_init);
late_initcall(blk_test_init);
module_exit(blk_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Test");
MODULE_DESCRIPTION("Block device test module for IO queue simulation");