Skip to contentSkip to conversation

kernel panic in sample block device driver

DISCUSSION Kumar amit mehta 3 participants 4 messages May 1, 2013 – May 2, 2013

lore
Share & tools
lore
#0 lore raw

Hi,

I'm new to block layer in linux and to learn the same, I'm trying to come up with a sample memory based block device driver, with which I can experiment and learn along the way. I'm referring to sample code from the linux tree [1] and assorted information available over the internet. My current module is causing system crash as soon I load it. Please take a look.

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
/*
* 1: register the major number
* 2: register callback functions for various capabilities
* 3: register a request function
* 4: disks characteristics information; gendisk */

#define RAMDK_MAJOR 166 //unique but static on my current machine as of now
#define BLKDEV_NAME "ramdk"
#define RAMDK_MINOR_NR 1

#define DISKSIZE 256*1024
#define NSECTORS 512
char buffer[DISKSIZE];

static struct gendisk *rdk = NULL;
static DEFINE_SPINLOCK(ramdk_sp_lock);
static struct request_queue *ramdk_queue = NULL;

int ramdk_open(struct block_device *, fmode_t);
int ramdk_release(struct gendisk *, fmode_t);

int ramdk_open(struct block_device *blk, fmode_t mode)

{
	printk(KERN_INFO "place holder for ramdisk's open method");
	return 0;
}

int ramdk_release(struct gendisk *gdk, fmode_t mode)

{
	printk(KERN_INFO "place holder for ramdisk's release method");
	return 0;
}

static const struct block_device_operations ramdk_op = {
.owner = THIS_MODULE,
.open = ramdk_open,
.release = ramdk_release,
};

/*
* block devices do not provide read()/write() routines like the char
* devices, instead they use request callback.
*/
static void rdk_request(struct request_queue *q)

{
	struct request *rq;

	/*
 	 * look at a request and then dequeue it
 	 */
	rq = blk_fetch_request(q);
	while (rq) {
		unsigned long offset = blk_rq_pos(rq);
		unsigned long nbytes  = blk_rq_cur_bytes(rq);
		int err = 0;
		while (nbytes) {
			if (rq_data_dir(rq) == READ) {
				memcpy(rq->buffer, (char *)offset, nbytes);
			} else if (rq_data_dir(rq) == WRITE) {
				memcpy((char *)offset, rq->buffer, nbytes);
			} else {
				printk(KERN_ERR "unknown operation\n");
			}
			nbytes -= offset;
		}
		if (!__blk_end_request_cur(rq, err))
			rq = blk_fetch_request(q);
	}
	return;
}

static int __init ramdk_init(void)

{
	int ret = -1;
	/*
 	 * blocking call. On success, assign an unused major number and add a entry in
 	 * /proc/devices.
 	 */
	if (register_blkdev(RAMDK_MAJOR, BLKDEV_NAME))
		return -EBUSY;

        printk(KERN_INFO "registered block device %s with major: %d", 
		BLKDEV_NAME, RAMDK_MAJOR);

	rdk = alloc_disk(RAMDK_MINOR_NR);
	if (!rdk) {
		ret = -ENOMEM;
		goto disk_alloc_fail;
	}

	rdk->fops = &ramdk_op;
	/*
 	 * HW perform I/O in the multiples of sectors(512Bytes, typically), whereas SW(FS, etc)
 	 * will work on block size(4k, typically). Therefore we need to tell the upper layers
 	 * about the capability of the hardware. This also sets the maximum number of sectors
 	 * that my hardware can receive per request.
 	 */
	set_capacity(rdk, DISKSIZE*2); //Capacity, in terms of sectors
	/*
         * returns request queue for the block device. protected using spin lock
         */
	ramdk_queue = blk_init_queue(rdk_request, &ramdk_sp_lock);
	if (!ramdk_queue)
		goto queue_fail;

	rdk->queue = ramdk_queue;   
	rdk->major = RAMDK_MAJOR;
	rdk->first_minor = 0;
	sprintf(rdk->disk_name, BLKDEV_NAME);
	rdk->private_data = buffer;
	/*
 	 * Going live now!!!
 	 */	
	add_disk(rdk);
	
	return 0;

queue_fail:

	printk(KERN_ERR "failed to allocate queue for %s",BLKDEV_NAME);

disk_alloc_fail:

	unregister_blkdev(RAMDK_MAJOR, BLKDEV_NAME);
	
	return ret;
}

static void __exit ramdk_exit(void)

{
        del_gendisk(rdk);
	put_disk(rdk);
   	blk_cleanup_queue(ramdk_queue);
	unregister_blkdev(RAMDK_MAJOR, BLKDEV_NAME); 
	printk(KERN_INFO "%s is offline now!!!",BLKDEV_NAME); 
}

module_init(ramdk_init);
module_exit(ramdk_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("goon");

Once this issue is fixed, I plan to add support for filesystem related operations such as mkfs, mount, etc.

[1] drivers/block/z2ram.c

-Amit

#1 lore raw

kernel panic logs?

Quoted 164 lines · Kumar amit mehta

Hi,

I'm new to block layer in linux and to learn the same, I'm trying to come up with a sample memory based block device driver, with which I can experiment and learn along the way. I'm referring to sample code from the linux tree [1] and assorted information available over the internet. My current module is causing system crash as soon I load it. Please take a look.

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
/*
* 1: register the major number
* 2: register callback functions for various capabilities
* 3: register a request function
* 4: disks characteristics information; gendisk */

#define RAMDK_MAJOR 166 //unique but static on my current machine as
of now
#define BLKDEV_NAME "ramdk"
#define RAMDK_MINOR_NR 1

#define DISKSIZE 256*1024
#define NSECTORS 512
char buffer[DISKSIZE];

static struct gendisk *rdk = NULL;
static DEFINE_SPINLOCK(ramdk_sp_lock);
static struct request_queue *ramdk_queue = NULL;

int ramdk_open(struct block_device *, fmode_t);
int ramdk_release(struct gendisk *, fmode_t);

int ramdk_open(struct block_device *blk, fmode_t mode)

{
        printk(KERN_INFO "place holder for ramdisk's open method");
        return 0;
}

int ramdk_release(struct gendisk *gdk, fmode_t mode)

{
        printk(KERN_INFO "place holder for ramdisk's release method");
        return 0;
}

static const struct block_device_operations ramdk_op = {
.owner = THIS_MODULE,
.open = ramdk_open,
.release = ramdk_release,
};

/*
* block devices do not provide read()/write() routines like the char
* devices, instead they use request callback.
*/
static void rdk_request(struct request_queue *q)

{
        struct request *rq;

        /*
         * look at a request and then dequeue it
         */
        rq = blk_fetch_request(q);
        while (rq) {
                unsigned long offset = blk_rq_pos(rq);
                unsigned long nbytes  = blk_rq_cur_bytes(rq);
                int err = 0;
                while (nbytes) {
                        if (rq_data_dir(rq) == READ) {
                                memcpy(rq->buffer, (char *)offset, nbytes);
                        } else if (rq_data_dir(rq) == WRITE) {
                                memcpy((char *)offset, rq->buffer, nbytes);
                        } else {
                                printk(KERN_ERR "unknown operation\n");
                        }
                        nbytes -= offset;
                }
                if (!__blk_end_request_cur(rq, err))
                        rq = blk_fetch_request(q);
        }
        return;
}

static int __init ramdk_init(void)

{
        int ret = -1;
        /*
         * blocking call. On success, assign an unused major number and
add a entry in
         * /proc/devices.
         */
        if (register_blkdev(RAMDK_MAJOR, BLKDEV_NAME))
                return -EBUSY;

        printk(KERN_INFO "registered block device %s with major: %d",
                BLKDEV_NAME, RAMDK_MAJOR);

        rdk = alloc_disk(RAMDK_MINOR_NR);
        if (!rdk) {
                ret = -ENOMEM;
                goto disk_alloc_fail;
        }

        rdk->fops = &ramdk_op;
        /*
         * HW perform I/O in the multiples of sectors(512Bytes,

typically), whereas SW(FS, etc)
* will work on block size(4k, typically). Therefore we need to
tell the upper layers
* about the capability of the hardware. This also sets the
maximum number of sectors
* that my hardware can receive per request.
*/
set_capacity(rdk, DISKSIZE*2); //Capacity, in terms of sectors /*
* returns request queue for the block device. protected using
spin lock

         */
        ramdk_queue = blk_init_queue(rdk_request, &ramdk_sp_lock);
        if (!ramdk_queue)
                goto queue_fail;

        rdk->queue = ramdk_queue;
        rdk->major = RAMDK_MAJOR;
        rdk->first_minor = 0;
        sprintf(rdk->disk_name, BLKDEV_NAME);
        rdk->private_data = buffer;
        /*
         * Going live now!!!
         */
        add_disk(rdk);

        return 0;

queue_fail:

        printk(KERN_ERR "failed to allocate queue for %s",BLKDEV_NAME);

disk_alloc_fail:

        unregister_blkdev(RAMDK_MAJOR, BLKDEV_NAME);

        return ret;
}

static void __exit ramdk_exit(void)

{
        del_gendisk(rdk);
        put_disk(rdk);
        blk_cleanup_queue(ramdk_queue);
        unregister_blkdev(RAMDK_MAJOR, BLKDEV_NAME);
        printk(KERN_INFO "%s is offline now!!!",BLKDEV_NAME);
}

module_init(ramdk_init);
module_exit(ramdk_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("goon");

Once this issue is fixed, I plan to add support for filesystem related operations such as mkfs, mount, etc.

Quoted 9 lines

[1] drivers/block/z2ram.c

-Amit

_______________________________________________
Kernelnewbies mailing list
Kernelnewbies at kernelnewbies.org
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20130501/035ace7e/attachment.html

#2 lore raw
In reply to Kumar amit mehta
Quoted 164 lines · Kumar amit mehta

Hi,

I'm new to block layer in linux and to learn the same, I'm trying to come up with a sample memory based block device driver, with which I can experiment and learn along the way. I'm referring to sample code from the linux tree [1] and assorted information available over the internet. My current module is causing system crash as soon I load it. Please take a look.

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
/*
* 1: register the major number
* 2: register callback functions for various capabilities
* 3: register a request function
* 4: disks characteristics information; gendisk */

#define RAMDK_MAJOR 166 //unique but static on my current machine as of
now
#define BLKDEV_NAME "ramdk"
#define RAMDK_MINOR_NR 1

#define DISKSIZE 256*1024
#define NSECTORS 512
char buffer[DISKSIZE];

static struct gendisk *rdk = NULL;
static DEFINE_SPINLOCK(ramdk_sp_lock);
static struct request_queue *ramdk_queue = NULL;

int ramdk_open(struct block_device *, fmode_t);
int ramdk_release(struct gendisk *, fmode_t);

int ramdk_open(struct block_device *blk, fmode_t mode)

{
	printk(KERN_INFO "place holder for ramdisk's open method");
	return 0;
}

int ramdk_release(struct gendisk *gdk, fmode_t mode)

{
	printk(KERN_INFO "place holder for ramdisk's release method");
	return 0;
}

static const struct block_device_operations ramdk_op = {
.owner = THIS_MODULE,
.open = ramdk_open,
.release = ramdk_release,
};

/*
* block devices do not provide read()/write() routines like the char
* devices, instead they use request callback.
*/
static void rdk_request(struct request_queue *q)

{
	struct request *rq;

	/*
(quoted diff) +0 -0
 	 * look at a request and then dequeue it
 	 */
	rq = blk_fetch_request(q);
	while (rq) {
		unsigned long offset = blk_rq_pos(rq);
		unsigned long nbytes  = blk_rq_cur_bytes(rq);
		int err = 0;
		while (nbytes) {
			if (rq_data_dir(rq) == READ) {
				memcpy(rq->buffer, (char *)offset, nbytes);
			} else if (rq_data_dir(rq) == WRITE) {
				memcpy((char *)offset, rq->buffer, nbytes);
			} else {
				printk(KERN_ERR "unknown operation\n");
			}
			nbytes -= offset;
		}
		if (!__blk_end_request_cur(rq, err))
			rq = blk_fetch_request(q);
	}
	return;
}

static int __init ramdk_init(void)

{
	int ret = -1;
	/*

* blocking call. On success, assign an unused major number and add a
entry in

(quoted diff) +0 -0
 	 * /proc/devices.
 	 */
	if (register_blkdev(RAMDK_MAJOR, BLKDEV_NAME))
		return -EBUSY;

        printk(KERN_INFO "registered block device %s with major: %d",
		BLKDEV_NAME, RAMDK_MAJOR);

	rdk = alloc_disk(RAMDK_MINOR_NR);
	if (!rdk) {
		ret = -ENOMEM;
		goto disk_alloc_fail;
	}

	rdk->fops = &ramdk_op;
	/*

* HW perform I/O in the multiples of sectors(512Bytes, typically),
whereas SW(FS, etc)
* will work on block size(4k, typically). Therefore we need to tell the
upper layers
* about the capability of the hardware. This also sets the maximum number
of sectors

(quoted diff) +0 -0
 	 * that my hardware can receive per request.
 	 */

set_capacity(rdk, DISKSIZE*2); //Capacity, in terms of sectors /*
* returns request queue for the block device. protected using spin
lock

         */
	ramdk_queue = blk_init_queue(rdk_request, &ramdk_sp_lock);
	if (!ramdk_queue)
		goto queue_fail;

	rdk->queue = ramdk_queue;
	rdk->major = RAMDK_MAJOR;
	rdk->first_minor = 0;
	sprintf(rdk->disk_name, BLKDEV_NAME);
	rdk->private_data = buffer;
	/*
(quoted diff) +0 -0
 	 * Going live now!!!
 	 */	
	add_disk(rdk);
	
	return 0;

queue_fail:

	printk(KERN_ERR "failed to allocate queue for %s",BLKDEV_NAME);

disk_alloc_fail:

	unregister_blkdev(RAMDK_MAJOR, BLKDEV_NAME);
	
	return ret;
}

static void __exit ramdk_exit(void)

{
        del_gendisk(rdk);
	put_disk(rdk);
   	blk_cleanup_queue(ramdk_queue);
	unregister_blkdev(RAMDK_MAJOR, BLKDEV_NAME);
	printk(KERN_INFO "%s is offline now!!!",BLKDEV_NAME);
}

module_init(ramdk_init);
module_exit(ramdk_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("goon");

Once this issue is fixed, I plan to add support for filesystem related operations such as mkfs, mount, etc.

Quoted 9 lines

[1] drivers/block/z2ram.c

-Amit

_______________________________________________
Kernelnewbies mailing list
Kernelnewbies at kernelnewbies.org
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

Hey Amit,

I'm not sure about what's wrong with it, but why not try to process each segment separately. i know it's more work but you should plan for that too.
In the mean time you can look at

https://github.com/pranjas/block_driver

I wrote this sometime back and its commented heavily. its also for in memory disk but you can set the block device sector size other than 512 bytes.

You might get a freeze kernel or crash when u unload it lemme know if that happens, or change the blk_end_request call to blk_end_request_all.

Hope it helps

                   -P.K.S
Signature · 1 line
Pranay Srivastava