#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/blkpg.h>
#include <linux/bio.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#define BLK_SIZE (10*1024*512)
#define SECTOR_SIZE (512)
MODULE_AUTHOR("Pingbo An");
MODULE_LICENSE("GPL v2");
struct vmem_device {
struct gendisk *disk;
struct request_queue *que;
void *buf;
spinlock_t lock;
ssize_t size;
};
struct vmem_device *vdev=NULL;
static int vmem_major;
static int vmem_open(struct block_device *bdev, fmode_t mode)
{
return 0;
}
static void vmem_release(struct gendisk *disk, fmode_t mode)
{
}
static int vmem_ioctl(struct block_device *bdev, fmode_t mode,
unsigned command, unsigned long argument)
{
return 0;
}
static int vmem_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
geo->cylinders=1;
geo->heads=1;
geo->sectors=BLK_SIZE/SECTOR_SIZE;
return 0;
}
static struct block_device_operations vmem_fops={
.owner=THIS_MODULE,
.getgeo=vmem_getgeo,
.ioctl=vmem_ioctl,
.open=vmem_open,
.release=vmem_release,
};
static int vmem_transfer(struct vmem_device *vdev, uint64_t pos, ssize_t size, void *buffer, int write)
{
if(write)
memcpy(vdev->buf+pos, buffer, size);
else
memcpy(buffer, vdev->buf+pos, size);
return 0;
}
static void vmem_request(struct request_queue *q){
struct request *req;
uint64_t pos=0;
ssize_t size=0;
struct bio_vec bvec;
int rv=0;
struct req_iterator iter;
void *kaddr=NULL;
while((req=blk_fetch_request(q)) != NULL){
spin_unlock_irq(q->queue_lock);
pos=blk_rq_pos(req)*SECTOR_SIZE;
size=blk_rq_bytes(req);
if(pos+size>vdev->size){
printk(KERN_WARNING "beyond addr\n");
rv=-EIO;
goto skip;
}
rq_for_each_segment(bvec, req, iter){
kaddr=kmap(bvec.bv_page);
rv=vmem_transfer(vdev, pos, bvec.bv_len, kaddr+bvec.bv_offset, rq_data_dir(req));
if(rv<0)
goto skip;
pos+=bvec.bv_len;
kunmap(bvec.bv_page);
}
skip:
blk_end_request_all(req, rv);
spin_lock_irq(q->queue_lock);
}
}
static int __init vmem_init(void)
{
struct gendisk *disk;
vmem_major=register_blkdev(0, "VMEM");
vdev=kzalloc(sizeof(struct vmem_device), GFP_KERNEL);
if(!vdev){
printk(KERN_WARNING "vmem_device: unable to allocate mem\n");
goto out;
}
vdev->size=BLK_SIZE;
vdev->buf=vmalloc(vdev->size);
if(vdev->buf==NULL){
printk(KERN_WARNING "failed to vmalloc vdev->buf\n");
goto out_dev;
}
spin_lock_init(&vdev->lock);
vdev->que=blk_init_queue(vmem_request, &vdev->lock);
if(vdev->que==NULL){
printk(KERN_WARNING "failed to init queue\n");
goto out_buf;
}
disk=alloc_disk(1);
if(disk==NULL){
printk(KERN_WARNING "failed to alloc disk\n");
goto out_que;
}
vdev->disk=disk;
disk->major=vmem_major;
disk->first_minor=1;
disk->fops=&vmem_fops;
disk->queue=vdev->que;
disk->private_data=vdev;
sprintf(disk->disk_name, "VMEM");
set_capacity(disk, BLK_SIZE/SECTOR_SIZE);
add_disk(disk);
printk(KERN_INFO "succeed to init\n");
return 0;
out_que:
blk_cleanup_queue(vdev->que);
out_buf:
vfree(vdev->buf);
out_dev:
kfree(vdev);
out:
unregister_blkdev(vmem_major, "VMEM");
return -1;
}
static void __exit vmem_exit(void)
{
del_gendisk(vdev->disk);
blk_cleanup_queue(vdev->que);
put_disk(vdev->disk);
vfree(vdev->buf);
kfree(vdev);
unregister_blkdev(vmem_major, "VMEM");
printk(KERN_INFO "module exit\n");
}
module_init(vmem_init);
module_exit(vmem_exit);