天天看点

移植sbull至kernel version > 2.6.31

移植sbull至kernel version > 2.6.31

     最近在看LDD3和ULK,学习kernel中块设备的驱动机制,kernel 2.6中block layer API已经变更了许多,主要有以下几点:

1. 对于request的请求处理机制,去除掉了原先的elv_next_request和blk_dequeue_request结构,取而代之的是blk_peek_request和blk_start_request,详见如下链接(http://lwn.net/Articles/333620/)。

2.对于block_device_operations中相关成员的修改,详见如下链接(http://lwn.net/Articles/246919/)。

3.对于与bio相关的操作宏的修改,去掉了bio_cur_sectors.

由于以上的修改,LDD3中示例的sbull驱动无法使用,下面是我针对2.6.31的内核对sbull驱动进行修改后的源码。

#include <linux/config.h>

#include <linux/module.h>

#include <linux/moduleparam.h>

#include <linux/init.h>

#include <linux/sched.h>

#include <linux/kernel.h>

#include <linux/slab.h>  

#include <linux/fs.h>  

#include <linux/errno.h>

#include <linux/timer.h>

#include <linux/types.h>

#include <linux/fcntl.h>

#include <linux/hdreg.h>

#include <linux/kdev_t.h>

#include <linux/vmalloc.h>

#include <linux/genhd.h>

#include <linux/blkdev.h>

#include <linux/buffer_head.h>

#include <linux/bio.h>

MODULE_LICENSE("Dual BSD/GPL");

static int sbull_major = 0;

module_param(sbull_major, int, 0);

static int hardsect_size = 512;

module_param(hardsect_size, int, 0);

static int nsectors = 1024;

module_param(nsectors, int, 0);

static int ndevices = 4;

module_param(ndevices, int, 0);

enum {

RM_SIMPLE = 0,

RM_FULL    = 1,

RM_NOQUEUE = 2,

};

static int request_mode = RM_SIMPLE;

module_param(request_mode, int, 0);

#define SBULL_MINORS 16

#define MINOR_SHIFT 4

#define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT

#define KERNEL_SECTOR_SIZE 512

#define INVALIDATE_DELAY 30*HZ

struct sbull_dev {

        int size;                      

        u8 *data;                      

        short users;                   

        short media_change;            

        spinlock_t lock;               

        struct request_queue *queue;   

        struct gendisk *gd;            

        struct timer_list timer;       

};

static struct sbull_dev *Devices = NULL;

static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,

   unsigned long nsect, char *buffer, int write)

{

unsigned long offset = sector*KERNEL_SECTOR_SIZE;

unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;

if ((offset + nbytes) > dev->size) {

   printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);

   return;

}

if (write)

   memcpy(dev->data + offset, buffer, nbytes);

else

   memcpy(buffer, dev->data + offset, nbytes);

}

static void sbull_request(struct request_queue *q)

{

struct request *req;

req = blk_fetch_request(q);

while (req != NULL) {

   struct sbull_dev *dev = req->rq_disk->private_data;

   if (! blk_fs_request(req)) {

    printk (KERN_NOTICE "Skip non-fs request\n");

    __blk_end_request_all(req, -EIO);

    continue;

   }

    //    printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",

    //        dev - Devices, rq_data_dir(req),

    //        req->sector, req->current_nr_sectors,

    //        req->flags);

   sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

   if(!__blk_end_request_cur(req, 0)) {

    req = blk_fetch_request(q);

   }

}

}

static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)

{

int i;

struct bio_vec *bvec;

sector_t sector = bio->bi_sector;

bio_for_each_segment(bvec, bio, i) {

   char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);

   sbull_transfer(dev, sector, bio_cur_bytes(bio)>>9 , 

    buffer, bio_data_dir(bio) == WRITE);

   sector += bio_cur_bytes(bio)>>9;

   __bio_kunmap_atomic(bio, KM_USER0);

}

return 0;

}

static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)

{

struct bio *bio;

int nsect = 0;

__rq_for_each_bio(bio, req) {

   sbull_xfer_bio(dev, bio);

   nsect += bio->bi_size/KERNEL_SECTOR_SIZE;

}

return nsect;

}

static void sbull_full_request(struct request_queue *q)

{

struct request *req;

int sectors_xferred;

struct sbull_dev *dev = q->queuedata;

req = blk_fetch_request(q);

while (req != NULL) {

   if (! blk_fs_request(req)) {

    printk (KERN_NOTICE "Skip non-fs request\n");

    __blk_end_request_all(req, -EIO);

    continue;

   }

   sectors_xferred = sbull_xfer_request(dev, req);

   if (!__blk_end_request_cur(req, 0)) {

    blk_fetch_request(q);

   }

}

}

static int sbull_make_request(struct request_queue *q, struct bio *bio)

{

struct sbull_dev *dev = q->queuedata;

int status;

status = sbull_xfer_bio(dev, bio);

bio_endio(bio, status);

return 0;

}

static int sbull_open(struct block_device *device, fmode_t mode)

{

struct sbull_dev *dev = device->bd_disk->private_data;

del_timer_sync(&dev->timer);

spin_lock(&dev->lock);

if (! dev->users) 

   check_disk_change(device);

dev->users++;

spin_unlock(&dev->lock);

return 0;

}

static int sbull_release(struct gendisk *disk, fmode_t mode)

{

struct sbull_dev *dev = disk->private_data;

spin_lock(&dev->lock);

dev->users--;

if (!dev->users) {

   dev->timer.expires = jiffies + INVALIDATE_DELAY;

   add_timer(&dev->timer);

}

spin_unlock(&dev->lock);

return 0;

}

int sbull_media_changed(struct gendisk *gd)

{

struct sbull_dev *dev = gd->private_data;

return dev->media_change;

}

int sbull_revalidate(struct gendisk *gd)

{

struct sbull_dev *dev = gd->private_data;

if (dev->media_change) {

   dev->media_change = 0;

   memset (dev->data, 0, dev->size);

}

return 0;

}

void sbull_invalidate(unsigned long ldev)

{

struct sbull_dev *dev = (struct sbull_dev *) ldev;

spin_lock(&dev->lock);

if (dev->users || !dev->data) 

   printk (KERN_WARNING "sbull: timer sanity check failed\n");

else

   dev->media_change = 1;

spin_unlock(&dev->lock);

}

int sbull_ioctl (struct block_device *device, fmode_t mode,

                 unsigned int cmd, unsigned long arg)

{

long size;

struct hd_geometry geo;

struct sbull_dev *dev = device->bd_disk->private_data;

switch(cmd) {

     case HDIO_GETGEO:

   size = dev->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 __user *) arg, &geo, sizeof(geo)))

    return -EFAULT;

   return 0;

}

return -ENOTTY;

}

static struct block_device_operations sbull_ops = {

.owner           = THIS_MODULE,

.open           = sbull_open,

.release = sbull_release,

.media_changed   = sbull_media_changed,

.revalidate_disk = sbull_revalidate,

.ioctl          = sbull_ioctl

};

static void setup_device(struct sbull_dev *dev, int which)

{

memset (dev, 0, sizeof (struct sbull_dev));

dev->size = nsectors*hardsect_size;

dev->data = vmalloc(dev->size);

if (dev->data == NULL) {

   printk (KERN_NOTICE "vmalloc failure.\n");

   return;

}

spin_lock_init(&dev->lock);

init_timer(&dev->timer);

dev->timer.data = (unsigned long) dev;

dev->timer.function = sbull_invalidate;

switch (request_mode) {

     case RM_NOQUEUE:

   dev->queue = blk_alloc_queue(GFP_KERNEL);

   if (dev->queue == NULL)

    goto out_vfree;

   blk_queue_make_request(dev->queue, sbull_make_request);

   break;

     case RM_FULL:

   dev->queue = blk_init_queue(sbull_full_request, &dev->lock);

   if (dev->queue == NULL)

    goto out_vfree;

   break;

     default:

   printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);

     case RM_SIMPLE:

   dev->queue = blk_init_queue(sbull_request, &dev->lock);

   if (dev->queue == NULL)

    goto out_vfree;

   break;

}

blk_queue_logical_block_size(dev->queue, hardsect_size);

dev->queue->queuedata = dev;

dev->gd = alloc_disk(SBULL_MINORS);

if (! dev->gd) {

   printk (KERN_NOTICE "alloc_disk failure\n");

   goto out_vfree;

}

dev->gd->major = sbull_major;

dev->gd->first_minor = which*SBULL_MINORS;

dev->gd->fops = &sbull_ops;

dev->gd->queue = dev->queue;

dev->gd->private_data = dev;

snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');

set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));

add_disk(dev->gd);

return;

out_vfree:

if (dev->data)

   vfree(dev->data);

}

static int __init sbull_init(void)

{

int i;

sbull_major = register_blkdev(sbull_major, "sbull");

if (sbull_major <= 0) {

   printk(KERN_WARNING "sbull: unable to get major number\n");

   return -EBUSY;

}

Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);

if (Devices == NULL)

   goto out_unregister;

for (i = 0; i < ndevices; i++) 

   setup_device(Devices + i, i);

return 0;

out_unregister:

unregister_blkdev(sbull_major, "sbd");

return -ENOMEM;

}

static void sbull_exit(void)

{

int i;

for (i = 0; i < ndevices; i++) {

   struct sbull_dev *dev = Devices + i;

   del_timer_sync(&dev->timer);

   if (dev->gd) {

    del_gendisk(dev->gd);

    put_disk(dev->gd);

   }

   if (dev->queue)

    blk_cleanup_queue(dev->queue);

   if (dev->data)

    vfree(dev->data);

}

unregister_blkdev(sbull_major, "sbull");

kfree(Devices);

}

module_init(sbull_init);

module_exit(sbull_exit);

以下是测试结果:

ls -lh

total 80K

-rw-rw-r-- 1 casualfish casualfish 12K 2010-07-21 12:10 alice

[[email protected] ~]# uname -a

Linux hitwh.edu.cn 2.6.31.1 #1 SMP Thu Oct 29 12:52:36 CST 2009 i686 i686 i386 GNU/Linux

[[email protected] ~]# dd if=/home/casualfish/alice of=/dev/sbulla bs=512 count=2

2+0 records in

2+0 records out

1024 bytes (1.0 kB) copied, 4.854e-05 s, 21.1 MB/s

[[email protected] ~]# dd if=/dev/sbulla of=/home/casualfish/file bs=512 count=2

2+0 records in

2+0 records out

1024 bytes (1.0 kB) copied, 0.000196812 s, 5.2 MB/s