块设备驱动注意事项

将ldd3(linxu device driver 3)中块设备驱动部分的代码在redhat6.3下编译时,会出现很多问题,主要是由于ldd3示例代码使用的内核版本较低(2.6.10版本),

对于块设备子系统,很多接口都已经发生了改变,

主要有如下变化:

1,elv_next_request已删除,使用如下方式从请求队列中获取请求

    struct request *req;

    req =  blk_fetch_request (q);

    while(req != NULL){

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

        if(req->cmd_type != REQ_TYPE_FS){

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

            blk_end_request_all(req,-EIO);

            continue;

        }

        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 = NULL;

        }

    }

其中blk_peek_request从队列中获取请求,但是不会从队列中删除该请求。

从队列中删除请求需要调用blk_start_request(),blk_fetch_request 相当于调用blk_peek_request,和blk_start_request()

具体参考:http://lwn.net/Articles/333620/,

2,end_request,改成了blk_end_request_all

3,现在不能直接获取request结构的sector和current_nr_sector;而是通过函数blk_rq_pos和blk_rq_cur_sectors获得,所以将对用两处改为:

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

4,bio_endio参数过多

5,blk_queue_hardsect_size定义已删除,使用blk_queue_logical_block_size(dev->queue, hardsect_size);

6,block_device_operation接口已经发生了非常大的变化

新接口如下:(该接口包含getgeo函数,从而不再需要在ioctl函数实现HDIO_GETGEO命令)

struct block_device_operations {

int (*open) (struct block_device *, fmode_t);

int (*release) (struct gendisk *, fmode_t);

int (*locked_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

int (*direct_access) (struct block_device *, sector_t,

void **, unsigned long *);

int (*media_changed) (struct gendisk *);

unsigned long long (*set_capacity) (struct gendisk *,

unsigned long long);

int (*revalidate_disk) (struct gendisk *);

int (*getgeo)(struct block_device *, struct hd_geometry *);

/* this callback is with swap_lock and sometimes page table lock held */

void (*swap_slot_free_notify) (struct block_device *, unsigned long);

struct module *owner;

};

编译通过后就可以使用该设备,例如

创建分区:

fdisk /dev/sbulla

创建文件系统

mkfs.ext3 /dev/sbulla1

修改后的代码如下:

#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> /* size_t */

#include <linux/fcntl.h> /* O_ACCMODE */

#include <linux/hdreg.h> /* HDIO_GETGEO */

#include <linux/kdev_t.h>

#include <linux/vmalloc.h>

#include <linux/genhd.h>

#include <linux/blkdev.h>

#include <linux/buffer_head.h> /* invalidate_bdev */

#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 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(req->cmd_type != REQ_TYPE_FS){

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

            blk_end_request_all(req,-EIO);

            continue;

        }

        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 = NULL;

        }


    }

}


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)/KERNEL_SECTOR_SIZE,buffer,bio_data_dir(bio) == WRITE);

      sector += bio_cur_bytes(bio)/KERNEL_SECTOR_SIZE;

      __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;

   struct sbull_dev *dev = q->queuedata;

   req = blk_fetch_request(q);

   while(req  != NULL){

       if(req->cmd_type != REQ_TYPE_FS){

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

             blk_end_request_all(req,-EIO);

            continue;

       }

       sbull_xfer_request(dev,req);

       if(!blk_end_request_cur(req,0)){

          req = NULL;

       }

   }

}


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 *bd, fmode_t mode)

{

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

    del_timer_sync(&dev->timer);

    spin_lock(&dev->lock);

    if(!dev->users)

        check_disk_change(bd);

    dev->users++;

    spin_unlock(&dev->lock);

    return 0;

}


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

{

    struct sbull_dev *dev = gd->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_change(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 *bd, fmode_t mode, unsigned cmd, unsigned long arg)

{

    return 0;

}

static int sbull_getgeo(struct block_device *bd, struct hd_geometry *geo)

{

    long size;

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

    size = dev->size *(hardsect_size / KERNEL_SECTOR_SIZE);

    geo->cylinders = (size & ~0x3f) >> 6;

    geo->heads = 4;

    geo->sectors = 16;

    geo->start = 4;

    return 0;


}

static struct block_device_operations sbull_ops = {

    .owner = THIS_MODULE,

    .open = sbull_open,

    .release = sbull_release,

    .media_changed = sbull_media_change,

    .revalidate_disk = sbull_revalidate,

    .ioctl = sbull_ioctl,

    .getgeo = sbull_getgeo

};


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

{

    printk(KERN_WARNING "sbull: step into into setup_device\n");

    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;

    case RM_SIMPLE:

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

        if(dev->queue == NULL)

            goto out_vfree;

        break;

    default:

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

    }

    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;

    printk(KERN_WARNING "sbull: start init\n");

    sbull_major = register_blkdev(sbull_major,"sbull");

    if (sbull_major <= 0) {

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

return -EBUSY;

}

printk(KERN_WARNING "sbull: start kmalloc\n");

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

if(Devices == NULL)

        goto out_unregister;

    printk(KERN_WARNING "sbull: start setup_device\n");

    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) {

if (request_mode == RM_NOQUEUE)

kobject_put(&(dev->queue)->kobj);

else

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);

你可能感兴趣的:(块设备驱动注意事项)