LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   more linux development newbie questions about sbull.c (https://www.linuxquestions.org/questions/programming-9/more-linux-development-newbie-questions-about-sbull-c-848321/)

eantoranz 12-04-2010 09:10 AM

more linux development newbie questions about sbull.c
 
Hi!

I'm working on the sbull.c code from http://www.kernel-labs.org/?q=blockdriver.

Because of the difference between the linux version used to develop that code at the time and what I'm using right now (kubuntu lucid) there appears to have been some api changes.

One of them is not having elv_next_request anymore but blk_fetch_request. But now I find more things have changed. What do I have to call instead now?

For example, end_request is not anymore. I find blk_end_request from linux/blkdev.h but it expects a third argument. There's another function called blk_end_request_cur that expects the same two arguments in sbull.c. Can I use that function instead?

eantoranz 12-04-2010 11:21 AM

Though I'm still not there yet, I think I'm getting close to it. The .c file can be compiled but I'm getting a warning that I'm sure will be a problem if I try to load the module.

The code is like this right now
Code:

/*
 * A sample, extra-simple block driver.
 *
 * Copyright 2003 Eklektix, Inc.  Redistributable under the terms
 * of the GNU GPL.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h> /* printk() */
#include <linux/fs.h>    /* everything... */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>

MODULE_LICENSE("Dual BSD/GPL");
static char *Version = "1.3";

static int major_num = 0;
module_param(major_num, int, 0);
static int hardsect_size = 512;
module_param(hardsect_size, int, 0);
static int nsectors = 1024;  /* How big the drive is */
module_param(nsectors, int, 0);

/*
 * We can tweak our hardware sector size, but the kernel talks to us
 * in terms of small sectors, always.
 */
#define KERNEL_SECTOR_SIZE 512

/*
 * Our request queue.
 */
static struct request_queue *Queue;

/*
 * The internal representation of our device.
 */
static struct sbd_device {
    unsigned long size;
    spinlock_t lock;
    u8 *data;
    struct gendisk *gd;
} Device;


/*
 * Handle an I/O request.
 */
static void sbd_transfer(struct sbd_device *dev, struct request *q)
{
    unsigned long offset = blk_rq_pos(q)*hardsect_size;
    unsigned long nbytes = blk_rq_cur_sectors(q)*hardsect_size;
   
    if ((offset + nbytes) > dev->size) {
        printk (KERN_NOTICE "sbd: Beyond-end write (%ld %ld)\n", offset, nbytes);
        return;
    }
    if (rq_data_dir(q))
        // write
        memcpy(dev->data + offset, q->buffer, nbytes);
    else
        // read
        memcpy(q->buffer, dev->data + offset, nbytes);
}

static void sbd_request(struct request_queue *q)
{
    struct request *req;

    while ((req = blk_fetch_request(q)) != NULL) {
        if (! blk_fs_request(req)) {
            printk (KERN_NOTICE "Skip non-CMD request\n");
            blk_end_request_cur(req, 0);
            continue;
        }
        sbd_transfer(&Device, req);
        blk_end_request_cur(req, 1);
    }
}



/*
 * Ioctl.
 */
int sbd_ioctl (struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
{
        long size;
        struct hd_geometry geo;

        switch(cmd) {
        /*
        * The only command we need to interpret is HDIO_GETGEO, since
        * we can't partition the drive otherwise.  We have no real
        * geometry, of course, so make something up.
        */
            case HDIO_GETGEO:
                size = Device.size*(hardsect_size/KERNEL_SECTOR_SIZE);
                geo.cylinders = (size & ~0x3f) >> 6;
                geo.heads = 4;
                geo.sectors = 16;
                geo.start = 4;
                if (copy_to_user((void *) arg, &geo, sizeof(geo)))
                        return -EFAULT;
                return 0;
    }

    return -ENOTTY; /* unknown command */
}




/*
 * The device operations structure.
 */
static struct block_device_operations sbd_ops = {
    .owner          = THIS_MODULE,
    .ioctl          = sbd_ioctl
};

static int __init sbd_init(void)
{
/*
 * Set up our internal device.
 */
    Device.size = nsectors*hardsect_size;
    spin_lock_init(&Device.lock);
    Device.data = vmalloc(Device.size);
    if (Device.data == NULL)
        return -ENOMEM;
/*
 * Get a request queue.
 */
    Queue = blk_init_queue(sbd_request, &Device.lock);
    if (Queue == NULL)
            goto out;
    blk_queue_logical_block_size(Queue, hardsect_size);
/*
 * Get registered.
 */
    major_num = register_blkdev(major_num, "sbd");
    if (major_num <= 0) {
        printk(KERN_WARNING "sbd: unable to get major number\n");
        goto out;
    }
/*
 * And the gendisk structure.
 */
    Device.gd = alloc_disk(16);
    if (! Device.gd)
        goto out_unregister;
    Device.gd->major = major_num;
    Device.gd->first_minor = 0;
    Device.gd->fops = &sbd_ops;
    Device.gd->private_data = &Device;
    strcpy (Device.gd->disk_name, "sbd0");
    set_capacity(Device.gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
    Device.gd->queue = Queue;
    add_disk(Device.gd);

    return 0;

  out_unregister:
    unregister_blkdev(major_num, "sbd");
  out:
    vfree(Device.data);
    return -ENOMEM;
}

static void __exit sbd_exit(void)
{
    del_gendisk(Device.gd);
    put_disk(Device.gd);
    unregister_blkdev(major_num, "sbd");
    blk_cleanup_queue(Queue);
    vfree(Device.data);
}

module_init(sbd_init);
module_exit(sbd_exit);

This is from sbd.c that I found somewhere from Jonathan Corbet. It's the same sbull driver so we're still talking about the same thing.

Now, if you look carefully, you'll see that I changed the definition of bsd_transfer from
Code:

static void sbd_transfer(struct sbd_device *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
to

Code:

static void sbd_transfer(struct sbd_device *dev, struct request *q)
That's because everything I needed to pass sbd_transfer was in the request so no point in passing separate things when I had everything in request (I hope not to be messing up).

Now I want to make sure I won't things right in a couple of members I had to change from the request.

Before it was:
Code:

unsigned long offset = sector*hardsect_size;
unsigned long nbytes = nsect*hardsect_size;

[b]sector was taken from the request as request->current_nr_sectors, nsect was req->buffer.
Now I have
Code:

unsigned long offset = blk_rq_pos(q)*hardsect_size;
unsigned long nbytes = blk_rq_cur_sectors(q)*hardsect_size;

[q] being the request as received in sbd_transfer. Those are the equivalent functions, right?

ANd finally, I'm getting a warning when I compile:
Code:

sbd.c:129: warning: initialization from incompatible pointer type
That's when I try to set the fops. I think it's because the definition of .ioctl is this function:
Code:

int sbd_ioctl (struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)

But when looking at blkdev.h it is like this:
Code:

int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
So the signatures are not the same. How do I correct this? Thanks in advance for your help.

Update

The original code is here: http://lwn.net/Articles/58720/

sriram87 12-04-2010 02:13 PM

There is a rewrite of the block device driver for the newer kernel. You can find it here
http://goo.gl/w2OZT

eantoranz 12-05-2010 10:41 AM

The code from here did work: http://blog.superpat.com/2010/05/04/...kernel-2-6-31/

It's an updated version of sbd for the new block layer API. I'll study it... thank you very much for your help!


All times are GMT -5. The time now is 08:04 AM.