Porting a simple ramdisk
January 11, 2010 02:37PM
Hello!

I am new to kernel hacking and I want to learn more about writing block device drivers.
Therefore, I thought about writing a simple ramdisk to get started, which should prove as a simple task -- at least, that's what I thought.
I tried to port a simple ramdisk driver that I found on the web.
I use the latest kernel version, 2.6.32.3 I work with Ubuntu version 9.10.

The original version of the ramdisk can be found under:

[blog.hydrasystemsllc.com]

The driver was presented in the Linux+ magazine, issue 3/2009 , so I assume that it worked for whatever kernel version was recent at that date.
Because there have been some changes on the block layer, I tried to port the driver (you can find the code at the ending of my post).
I didn't succeed though, the driver freezes the system. Since I am new to writing kernel code, I don't have experience in debugging driver code. So I'd be grateful if you could see over my changes and hint at any mistakes that I made.

I made following changes:

* Replaced elv_next_request with blk_fetch_request

* Replaced end_request (rq, 0)/end_request(rq, 1) with blk_end_request_all at
the end of the loop (because - for now - I do not care whether the request or parts if it were completed
successfully or not ).

* Replaced .unlocked_ioctl = rx_unlocked_ioctl with .locked_ioctl = rx_unlocked_ioctl
I also changed the signature of the corresponding function signature from:

static long rx_unlocked_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)

to

static int
rx_unlocked_ioctl (struct block_device* bdev, fmode_t mode, unsigned int cmd, unsigned long arg) {


* Replaced rq->sector with blk_rq_pos

* Replaced rq->current_nr_sectors with blk_rq_cur_sectors
I assume current_nr_sectors referred to the number of sectors in the current segment.


* blk_queue_hardsect_size (rx_request_queue, BYTES_PER_SECTOR)

I don't really know what to do about that. This function does not exist anymore. Is it required?
I tried to replace it with:

blk_queue_logical_block_size(rx_request_queue, BYTES_PER_SECTOR)

I read somewhere that the logical block size only changes the alignment, but not the actual sector size.
(Is block size and sector size the same?)

* I am not sure what to do about:

ptr = rx_dev + blk_rq_pos(rq) * q->hardsect_size;
size = blk_rq_cur_sectors(rq) * q->hardsect_size;

Seems that the hardsect_size field was removed from the request_queue struct (declared as q).

Also, I am not sure about the arithmetics of ptr and size. I assume multiplying with 512 (as sectors are in the unit 512 byte) will do the trick?


I'd be grateful if you could help me out.


Here's the code:

/* rxd.c */

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/errno.h>

#define VERSION_STR          "0.1"
#define COPYRIGHT            "Copyright 2009 Petros Koutoupis"
#define DRIVER_AUTHOR        "Petros Koutoupis"
#define DRIVER_DESC          "This is a generic RAM disk block device driver."

#define DEVICE_NAME          "rxdrv"
#define BYTES_PER_SECTOR     512

static int rxdrv_ma_no = 0, diskmb = 64;
static int disk_size = 0;
static char *rx_dev;
static struct gendisk *rx_gd;
static spinlock_t lock;

static struct request_queue *rx_request_queue;

/**
 * function: rx_request()
 * description: read/write request routine handler.
 * return: (int)
 */
static void
rx_request (struct request_queue *q){
    struct request *rq;
    int size;
    char *ptr;

    printk(KERN_INFO "rxd: debug: entering %s\n", __func__);

 /*   while ((rq = elv_next_request (q))) { */ 
	  while ((rq = blk_fetch_request (q))) {
        if (!blk_fs_request (rq)) {
            printk ("rxd: debug: Unsupported fs request: skipping.\n" );
           /* end_request (rq, 0); */
            continue;
        }

        ptr = rx_dev + blk_rq_pos(rq) * 512;
        size = blk_rq_cur_sectors(rq) * q->hardsect_size;

        if ((ptr + size) > (rx_dev + disk_size)) {
            printk ("rxd: debug: reached end of device\n" );
            /* end_request (rq, 0); */
            continue;
        }

        if (rq_data_dir (rq)) {
            printk ("rxd: debug: Writing at sector %d, %u sectors \n",
                    (int)blk_rq_pos(rq), blk_rq_cur_sectors(rq));
            memcpy (ptr, rq->buffer, size);
        } else {
            printk ("rxd:debug: Reading at sector %d, %u sectors \n",
                    (int)blk_rq_pos(rq), blk_rq_cur_sectors(rq));
            memcpy (rq->buffer, ptr, size);
        }

        /* end_request (rq, 1); */
    }

	blk_end_request_all(rq, 0);
    printk ("rxd: debug: exiting %s\n", __func__);
}

/**
 * function: rx_unlocked_ioctl()
 * description: ioctl routine.
 * return: (int)
 */
static int
rx_unlocked_ioctl (struct block_device* bdev, fmode_t mode, unsigned int cmd, unsigned long arg) {
    long size;

    printk(KERN_INFO "rxd: debug: entering %s\n", __func__);
    printk ("rxd: debug: cmd=%d\n", cmd);

    switch (cmd) {
    case BLKGETSIZE:
        printk ("rxd: debug: request = BLKGETSIZE\n" );
        size = 131072;         /* number of sectors which equate to 64 MB */

        if (copy_to_user ((void __user *)arg, &size, sizeof (size)))
            return -EFAULT;

        return 0;
    }

    printk ("rxd:debug: unsupported command: return -ENOTTY\n" );

    return -ENOTTY;             /* unknown command */
}

static struct block_device_operations rxdrv_fops = {
    .owner = THIS_MODULE,
    /* .unlocked_ioctl = rx_unlocked_ioctl */
     .locked_ioctl = rx_unlocked_ioctl
};

/**
 * function: init_rxd()
 * description: Init routine for module insertion.
 * return: (int)
 */
static int
__init init_rxd (void){
    printk(KERN_INFO "rxd: debug: entering %s\n", __func__);

    disk_size = diskmb * 1024 * 1024;

    spin_lock_init (&lock);

    if (!(rx_dev = vmalloc (disk_size)))
        return -ENOMEM;

    if (!(rx_request_queue = blk_init_queue (rx_request, &lock))) {
        vfree (rx_dev);
        return -ENOMEM;
    }

    /* blk_queue_hardsect_size (rx_request_queue, BYTES_PER_SECTOR); */
	blk_queue_logical_block_size (rx_request_queue, BYTES_PER_SECTOR);

    rxdrv_ma_no = register_blkdev (rxdrv_ma_no, DEVICE_NAME);
    if (rxdrv_ma_no < 0){
        printk ("Failed registering rxdrv, returned %d\n", rxdrv_ma_no);
        vfree (rx_dev);
        return rxdrv_ma_no;
    }

    if (!(rx_gd = alloc_disk (16))){
        unregister_blkdev (rxdrv_ma_no, DEVICE_NAME);
        vfree (rx_dev);
        return -ENOMEM;
    }

    rx_gd->major = rxdrv_ma_no;
    rx_gd->first_minor = 0;
    rx_gd->fops = &rxdrv_fops;
    strcpy (rx_gd->disk_name, DEVICE_NAME);
    rx_gd->queue = rx_request_queue;
    set_capacity (rx_gd, disk_size / BYTES_PER_SECTOR);
    add_disk (rx_gd);

    printk ("rxd: debug: Device successfully registered: Major No. = %d\n", rxdrv_ma_no);
    printk ("rxd: debug: Capacity of RAM disk is: %d MB\n", diskmb);

    return 0;
}

/**
 * function: exit_rxd()
 * description: Exit routine for module removal.
 * return: (void)
 */
static void
__exit exit_rxd (void){
    printk(KERN_INFO "rxd: debug: entering %s\n", __func__);
    del_gendisk (rx_gd);
    put_disk (rx_gd);
    unregister_blkdev (rxdrv_ma_no, DEVICE_NAME);
    printk ("rxd: debug: Module successfully unloaded: Major No. = %d\n", rxdrv_ma_no);
    blk_cleanup_queue (rx_request_queue);
    vfree (rx_dev);
}

module_init(init_rxd);
module_exit(exit_rxd);

MODULE_LICENSE("GPL" );
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(VERSION_STR);
MODULE_INFO(Copyright, COPYRIGHT);



Edited 2 time(s). Last edit at 01/11/2010 02:41PM by Gerty3000.
Petros Koutoupis
Re: Porting a simple ramdisk
March 12, 2010 12:01PM
I think it is great that you are playing with a version of my Linux RAM Disk driver. Note that an even later version of code exists with some minor updates: [blog.hydrasystemsllc.com]

Currently, I do not have time to update the module to the latest kernel revision(s). But if you do end up getting something to work, I would love for it to be hosted on Launchpad or Sourceforge as an educational project. The current version was written for kernel 2.6.26.x

You can always contact me at pkoutoupis@hydrasystemsllc.com. Just make sure you write an appropriate subject for the e-mail and I know that it is related to this project.

Petros
Re: Porting a simple ramdisk
January 19, 2011 12:37AM
Hi,

I am new to kernel programming. I was trying to compile the above code with only init() and exit(), but i got struck in add_disk(). Could you please help me.



test.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>



#define KERNEL_SECTOR_SIZE 512

#define DEVICE_NAME "my_hd0"
#define BYTES_PER_SECTOR 512

static int major_no = 0, diskmb = 64;
static int disk_size = 0;
static char *my_dev;
static struct gendisk *my_gd;
static spinlock_t lock;

static struct request_queue *Queue;

static struct block_device_operations mydev_fops =
{
.owner = THIS_MODULE,

};



static void my_request (struct request_queue *q)
{

}

static int __init init_dev(void)
{
printk(KERN_INFO "my_dev : debug: Entering %s\n",__func__);
disk_size = diskmb * 1024 * 1024;
spin_lock_init(&lock);

if (!(my_dev = vmalloc (disk_size)))
return -ENOMEM;

if (!(Queue = blk_init_queue (my_request, &lock)))
{
vfree (my_dev);
return -ENOMEM;
}
blk_queue_logical_block_size (Queue, BYTES_PER_SECTOR);

major_no = register_blkdev (major_no, DEVICE_NAME);

if (major_no < 0)
{
printk ("Failed registering my_hd0, returned %d\n", major_no);
vfree (my_dev);
return major_no;
}
if (!(my_gd = alloc_disk (16)))
{
unregister_blkdev (major_no, DEVICE_NAME);
vfree (my_dev);
return -ENOMEM;
}


my_gd->major = major_no;
my_gd->first_minor = 0;
my_gd->fops = &mydev_fops;
strcpy (my_gd->disk_name, DEVICE_NAME);
my_gd->queue = Queue;
set_capacity (my_gd, disk_size / BYTES_PER_SECTOR);

add_disk (my_gd);

printk ("my_dev: debug: Device successfully registered: Major No. = %d\n", major_no);
printk ("my_dev: debug: Capacity of RAM disk is: %d MB\n", diskmb);

return 0;


}


static void __exit exit_dev(void)
{
printk(KERN_INFO "my_dev: debug: Entering %s\n", __func__);
del_gendisk (my_gd);
put_disk (my_gd);
unregister_blkdev (major_no, DEVICE_NAME);
printk ("my_dev: debug: Module successfully unloaded: Major No. = %d\n", major_no);
blk_cleanup_queue (Queue);
vfree (my_dev);
}

module_init(init_dev);
module_exit(exit_dev);


MODULE_LICENSE("GPL"winking smiley;

















~Kali
Re: Porting a simple ramdisk
January 09, 2019 07:41AM
Interesting... but could anyone do that without review to assess the quality of the service provided? I don't think so. Thanks this https://essayexplorer.com/review/ukwritings/ could help to do that.
Author:

Your Email:


Subject:


Spam prevention:
Please, solve the mathematical question and enter the answer in the input field below. This is for blocking bots that try to post this form automatically.
Question: how much is 7 plus 14?
Message: