Source

NetworkBlockDevice / DiskOnRAM / ram_block.c

Full commit
/* Disk on RAM Driver */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
#include <linux/kthread.h>

#include "ram_device.h"

#define RB_FIRST_MINOR 0
#define RB_MINOR_CNT 16

static u_int rb_major = 0;

/* 
 * The internal structure representation of our Device
 */
static struct rb_device {
    /* Size is the size of the device (in sectors) */
    unsigned int size;
    /* For exclusive access to our request queue */
    spinlock_t lock;
    /* Our request queue */
    struct request_queue *rb_queue;
    /* This is kernel's representation of an individual disk device */
    struct gendisk *rb_disk;
} rb_dev;

struct request_info {
    sector_t sector_off;
    u8 *buffer;
    unsigned int sectors;
    struct request *req;
};

 static void nbd_end_request(struct request *req)
 {
         int error = req->errors ? -EIO : 0;
         //struct request_queue *q = req->q;
         //unsigned long flags;
 
         //spin_lock_irqsave(q->queue_lock, flags);
         blk_end_request_cur(req, error);
         //spin_unlock_irqrestore(q->queue_lock, flags);
 }

int kThread_fun_read(void *data) {
    struct request_info *info = (struct request_info *) data;
    ramdevice_read(info->sector_off, info->buffer, info->sectors);
    nbd_end_request(info->req);
    //blk_end_request_cur(info->req, 0);
    return 0;
}

int kThread_fun_write(void *data) {
    struct request_info *info = (struct request_info *) data;
    ramdevice_write(info->sector_off, info->buffer, info->sectors);
    //blk_end_request_cur(info->req, 0);
    nbd_end_request(info->req);
    return 0;
}

static int rb_open(struct block_device *bdev, fmode_t mode) {
    unsigned unit = iminor(bdev->bd_inode);

    printk(KERN_INFO "rb: Device is opened\n");
    printk(KERN_INFO "rb: Inode number is %d\n", unit);

    if (unit > RB_MINOR_CNT)
        return -ENODEV;
    return 0;
}

static int rb_close(struct gendisk *disk, fmode_t mode) {
    printk(KERN_INFO "rb: Device is closed\n");
    return 0;
}



/* 
 * Actual Data transfer
 */
static int rb_transfer(struct request *req) {
    //struct rb_device *dev = (struct rb_device *)(req->rq_disk->private_data);

    int dir = rq_data_dir(req);
    sector_t start_sector = blk_rq_pos(req);
    unsigned int sector_cnt = blk_rq_sectors(req);

    struct bio_vec *bv;
    struct req_iterator iter;

    sector_t sector_offset;
    unsigned int sectors;
    u8 *buffer;

    int ret = 0;

    //printk(KERN_DEBUG "rb: Dir:%d; Sec:%lld; Cnt:%d\n", dir, start_sector, sector_cnt);

    sector_offset = 0;

    rq_for_each_segment(bv, req, iter) {
        buffer = page_address(bv->bv_page) + bv->bv_offset;
        if (bv->bv_len % RB_SECTOR_SIZE != 0) {
            printk(KERN_ERR "rb: Should never happen: "
                    "bio size (%d) is not a multiple of RB_SECTOR_SIZE (%d).\n"
                    "This may lead to data truncation.\n",
                    bv->bv_len, RB_SECTOR_SIZE);
            ret = -EIO;
        }
        sectors = bv->bv_len / RB_SECTOR_SIZE;
        printk(KERN_DEBUG "rb: Sector Offset: %lu; Buffer: %p; Length: %d sectors\n",
                sector_offset, buffer, sectors);
        if (dir == WRITE) /* Write to the device */ {
            struct request_info *write_info = kmalloc(sizeof (struct request_info), GFP_KERNEL);
            //info->sector_off, info->buffer, info->sectors
            write_info->sector_off = start_sector + sector_offset;
            write_info->buffer = buffer;
            write_info->sectors = sectors;
            write_info->req = req;
            kthread_run(kThread_fun_write, write_info, "write_thread");
            //ramdevice_write(start_sector + sector_offset, buffer, sectors);
        } else /* Read from the device */ {
            struct request_info *read_info = kmalloc(sizeof (struct request_info), GFP_KERNEL);
            //info->sector_off, info->buffer, info->sectors
            read_info->sector_off = start_sector + sector_offset;
            read_info->buffer = buffer;
            read_info->sectors = sectors;
            read_info->req = req;
            kthread_run(kThread_fun_read, read_info, "read_thread");
        }
        sector_offset += sectors;
    }
    if (sector_offset != sector_cnt) {
        printk(KERN_ERR "rb: bio info doesn't match with the request info");
        ret = -EIO;
    }

    return ret;
}

/*
 * Represents a block I/O request for us to execute
 */
static void rb_request(struct request_queue *q) 
        __releases(q->queue_lock) __acquires(q->queue_lock) 
{
    struct request *req;
    int ret;

    /* Gets the current request from the dispatch queue */
    while ((req = blk_fetch_request(q)) != NULL) {

        //spin_unlock_irq(q->queue_lock);
        //spin_lock(q->queue_lock);
        ret = rb_transfer(req);
        //spin_unlock(q->queue_lock);
        //spin_lock_irq(q->queue_lock);
        //__blk_end_request_all(req, ret);
        //__blk_end_request(req, ret, blk_rq_bytes(req));
    }
}

/* 
 * These are the file operations that performed on the ram block device
 */
static struct block_device_operations rb_fops ={
    .owner = THIS_MODULE,
    .open = rb_open,
    .release = rb_close,
};

/* 
 * This is the registration and initialization section of the ram block device
 * driver
 */
static int __init rb_init(void) {
    int ret;

    /* Set up our RAM Device */
    if ((ret = ramdevice_init()) < 0) {
        return ret;
    }
    rb_dev.size = ret;

    /* Get Registered */
    rb_major = register_blkdev(rb_major, "rb");
    if (rb_major <= 0) {
        printk(KERN_ERR "rb: Unable to get Major Number\n");
        ramdevice_cleanup();
        return -EBUSY;
    }

    /* Get a request queue (here queue is created) */
    spin_lock_init(&rb_dev.lock);
    rb_dev.rb_queue = blk_init_queue(rb_request, &rb_dev.lock);
    if (rb_dev.rb_queue == NULL) {
        printk(KERN_ERR "rb: blk_init_queue failure\n");
        unregister_blkdev(rb_major, "rb");
        ramdevice_cleanup();
        return -ENOMEM;
    }

    /*
     * Add the gendisk structure
     * By using this memory allocation is involved, 
     * the minor number we need to pass bcz the device 
     * will support this much partitions 
     */
    rb_dev.rb_disk = alloc_disk(RB_MINOR_CNT);
    if (!rb_dev.rb_disk) {
        printk(KERN_ERR "rb: alloc_disk failure\n");
        blk_cleanup_queue(rb_dev.rb_queue);
        unregister_blkdev(rb_major, "rb");
        ramdevice_cleanup();
        return -ENOMEM;
    }

    /* Setting the major number */
    rb_dev.rb_disk->major = rb_major;
    /* Setting the first mior number */
    rb_dev.rb_disk->first_minor = RB_FIRST_MINOR;
    /* Initializing the device operations */
    rb_dev.rb_disk->fops = &rb_fops;
    /* Driver-specific own internal data */
    rb_dev.rb_disk->private_data = &rb_dev;
    rb_dev.rb_disk->queue = rb_dev.rb_queue;
    /*
     * You do not want partition information to show up in 
     * cat /proc/partitions set this flags
     */
    //rb_dev.rb_disk->flags = GENHD_FL_SUPPRESS_PARTITION_INFO;
    sprintf(rb_dev.rb_disk->disk_name, "rb");
    /* Setting the capacity of the device in its gendisk structure */
    set_capacity(rb_dev.rb_disk, rb_dev.size);

    /* Adding the disk to the system */
    add_disk(rb_dev.rb_disk);
    /* Now the disk is "live" */
    printk(KERN_INFO "rb: Ram Block driver initialised (%d sectors; %d bytes)\n",
            rb_dev.size, rb_dev.size * RB_SECTOR_SIZE);

    return 0;
}

/*
 * This is the unregistration and uninitialization section of the ram block
 * device driver
 */
static void __exit rb_cleanup(void) {
    del_gendisk(rb_dev.rb_disk);
    put_disk(rb_dev.rb_disk);
    blk_cleanup_queue(rb_dev.rb_queue);
    unregister_blkdev(rb_major, "rb");
    ramdevice_cleanup();
}

module_init(rb_init);
module_exit(rb_cleanup);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email@sarika-pugs.com>");
MODULE_DESCRIPTION("Ram Block Driver");
MODULE_ALIAS_BLOCKDEV_MAJOR(rb_major);