今天我们使用内存来虚拟一个8M的硬盘。
驱动代码:
#include <linux/bio.h>
#include <linux/init.h> /* Needed for the macros */
#include <linux/kernel.h> /* Needed for pr_info() */
#include <linux/module.h> /* Needed by all modules */
#include <linux/sched.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <uapi/linux/hdreg.h>
#define HEADS 4
#define SECTORS 16
#define CYLINDERS 256
#define SECTORS_SIZE 512
#define SECTORS_TOTAL (HEADS * SECTORS * CYLINDERS)
//8M
#define VDISK_SZIE (SECTORS_TOTAL * SECTORS_SIZE)
#define DEVICE_NAME "mydisk"
/**
*
* https://linux-kernel-labs.github.io/refs/heads/master/labs/block_device_drivers.html
*
* https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch16.html
*
* https://bootlin.com/doc/legacy/block-drivers/block_drivers.pdf
*
*
* https://kernel.dk/blk-mq.pdf
*
* A block is a fixed-size chunk of data, the size being determined by the kernel.
* Blocks are often 4096 bytes, but that value can vary depending
* on the architecture and the exact filesystem being used
*
*
* sudo fdisk /dev/mydisk
* then n p w
* sudo mkfs.ext2 /dev/mydisk1
* sudo mount /dev/mydisk1 /tmp
* sudo vi to write to txt file.
* then use cat to verify.
*
*
*
*/
static int major;
struct vdsk_dev {
unsigned long size;
u8 *data;
spinlock_t lock ;
struct gendisk *mydisk;
struct request_queue *queue;
};
struct vdsk_dev *vdsk;
int myopen(struct block_device *dev, fmode_t mode) {
return 0;
}
void myrelease(struct gendisk *disk, fmode_t mode) {
}
int mygetgeo(struct block_device *dev, struct hd_geometry *geo) {
pr_err("mygetgeo");
geo->heads = HEADS;
geo->sectors = SECTORS;
geo->cylinders = CYLINDERS;
geo->start = 0;
return 0;
}
/* This function also called from IRQ context */
static void my_request(struct request_queue *q)
{
struct vdsk_dev *mydev = q->queuedata;
struct request *req;
u32 sect_num, sect_cnt;
struct bio *bio;
struct bio_vec bvec;
struct bvec_iter iter;
char *buffer;
unsigned long offset;
unsigned long nbytes;
int segment = 0;
int cbio =0;
req = blk_fetch_request(q);
pr_err("my_request start");
while (req != NULL) {
sect_num = blk_rq_pos(req);
/* deal whole segments */
sect_cnt = blk_rq_sectors(req);
__rq_for_each_bio(bio,req){
segment = 0;
cbio++;
pr_err("now process bio: %d",cbio);
bio_for_each_segment(bvec,bio,iter){
segment++;
pr_err("now process segment: %d",segment);
buffer = __bio_kmap_atomic(bio,iter);
offset =iter.bi_sector * SECTORS_SIZE ;
nbytes = bvec.bv_len ;
if (bio_data_dir(bio) == WRITE) {
pr_err("my_request WRITE");
memcpy(mydev->data + offset, buffer, nbytes);
} else {
pr_err("my_request read");
memcpy(buffer, mydev->data + offset, nbytes);
}
__bio_kunmap_atomic(bio);
}
}
if(!__blk_end_request_cur(req,0)){
req = blk_fetch_request(q);
}
}
pr_err("my_request end");
}
static const struct block_device_operations my_bd_ops = { .owner = THIS_MODULE,
.open = myopen, .release = myrelease, .getgeo = mygetgeo, };
static __init int my_init(void) {
int ret;
ret = -EBUSY;
major = register_blkdev(0,DEVICE_NAME);
if(major<=0){
pr_err("register_blkdev fail");
goto err;
}
pr_err("SECTORS_TOTAL %d",SECTORS_TOTAL);
vdsk = kzalloc(sizeof(struct vdsk_dev),GFP_KERNEL);
vdsk->size = VDISK_SZIE;
vdsk->data = vmalloc(VDISK_SZIE);
pr_err("00000000000000000");
ret = -ENOMEM;
vdsk->mydisk = alloc_disk(4);
pr_err("alloc_disk");
if (!vdsk->mydisk)
goto out_disk;
vdsk->mydisk->major = major;
vdsk->mydisk->first_minor = 0;
vdsk->mydisk->fops = &my_bd_ops;
spin_lock_init(&vdsk->lock);
vdsk->queue = blk_init_queue(my_request,&vdsk->lock);
pr_err("blk_init_queue");
if (!vdsk->queue)
goto out_queue;
blk_queue_logical_block_size(vdsk->queue, SECTORS_SIZE);
vdsk->queue->queuedata = vdsk;
vdsk->mydisk->queue = vdsk->queue;
vdsk->mydisk->private_data = vdsk ;
sprintf(vdsk->mydisk->disk_name,"mydisk");
set_capacity(vdsk->mydisk,SECTORS_TOTAL);
add_disk(vdsk->mydisk);
pr_err("add_disk");
pr_err("my_init end ");
return 0;
out_queue:
pr_err("out_queue");
put_disk(vdsk->mydisk);
out_disk:
pr_err("out_disk");
unregister_blkdev(major, DEVICE_NAME);
err:
return ret;
}
static void __exit my_exit(void) {
del_gendisk(vdsk->mydisk);
blk_cleanup_queue(vdsk->queue);
put_disk(vdsk->mydisk);
unregister_blkdev(major,DEVICE_NAME);
vfree(vdsk->data);
kfree(vdsk);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
MODULE_DESCRIPTION("andy one-key driver");
MODULE_ALIAS("one-key");
模块编译安装之后,执行以下命令:
1 分区
sudo fdisk /dev/mydisk
then n p w
2 建立文件系统
sudo mkfs.ext2 /dev/mydisk1
3挂载文件系统
sudo mount /dev/mydisk1 /tmp
4 测试写入以及读。
sudo vi to write to txt file.
then use cat to verify.